package approval

import (
	"context"
	"strconv"
	"strings"
	"time"

	"gorm.io/gorm"

	approvaldomain "github.com/break/junhong_cmp_fiber/internal/domain/approval"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

// CreationService 实现业务侧 Approval Port，并保持渠道前置检查与业务事务分离。
type CreationService struct {
	providers    ProviderPort
	repositories RepositoryProvider
	eventWriter  SubmissionEventWriter
	now          func() time.Time
}

// NewCreationService 创建通用审批申请创建用例。
func NewCreationService(
	providers ProviderPort,
	repositories RepositoryProvider,
	eventWriter SubmissionEventWriter,
	now func() time.Time,
) *CreationService {
	if now == nil {
		now = time.Now
	}
	return &CreationService{providers: providers, repositories: repositories, eventWriter: eventWriter, now: now}
}

// Prepare 在任何业务事实写入前确认 Adapter、场景和真实发起身份可用。
func (s *CreationService) Prepare(ctx context.Context, request PrepareRequest) (Preparation, error) {
	if s == nil || s.providers == nil || s.repositories == nil || s.eventWriter == nil {
		return Preparation{}, errors.New(errors.CodeServiceUnavailable, "审批能力尚未配置")
	}
	request.BusinessType = strings.TrimSpace(request.BusinessType)
	request.CorrelationID = strings.TrimSpace(request.CorrelationID)
	if request.BusinessType == "" || request.SubmitterAccountID == 0 || request.CorrelationID == "" {
		return Preparation{}, errors.New(errors.CodeInvalidParam)
	}
	providerContext, err := s.providers.Prepare(ctx, request)
	if err != nil {
		return Preparation{}, err
	}
	providerContext.Provider = strings.TrimSpace(providerContext.Provider)
	if providerContext.Provider == "" {
		return Preparation{}, errors.New(errors.CodeServiceUnavailable, "审批渠道未返回有效 provider")
	}
	return Preparation{
		provider: providerContext.Provider, businessType: request.BusinessType,
		submitterAccountID: request.SubmitterAccountID, correlationID: request.CorrelationID,
		expiresAt: s.now().UTC().Add(constants.ApprovalPreparationTTL), issuer: s,
		providerContext: providerContext,
	}, nil
}

// CreateInTx 使用调用方业务事务原子创建通用实例、渠道上下文和提交 Outbox。
func (s *CreationService) CreateInTx(ctx context.Context, tx *gorm.DB, request CreateRequest) (Reference, error) {
	if s == nil || tx == nil || s.repositories == nil || s.providers == nil || s.eventWriter == nil {
		return Reference{}, errors.New(errors.CodeInternalError, "通用审批创建用例未完整配置")
	}
	now := s.now().UTC()
	if err := s.validatePreparation(request, now); err != nil {
		return Reference{}, err
	}
	instance, err := approvaldomain.NewInstance(approvaldomain.NewInstanceParams{
		BusinessType: request.BusinessType, BusinessID: request.BusinessID,
		SubmitterAccountID: request.SubmitterAccountID, SubmitterSnapshot: request.SubmitterSnapshot,
		Provider: request.Preparation.provider, RequestSnapshot: request.RequestSnapshot,
		CorrelationID: request.CorrelationID,
	}, now)
	if err != nil {
		return Reference{}, err
	}
	repository := s.repositories.ForDB(tx)
	if repository == nil {
		return Reference{}, errors.New(errors.CodeInternalError, "通用审批 Repository 未配置")
	}
	if err := repository.Create(ctx, instance); err != nil {
		return Reference{}, err
	}
	if err := s.providers.CreateContextInTx(ctx, tx, request.Preparation.providerContext, instance.ID); err != nil {
		return Reference{}, err
	}
	event := SubmissionRequestedEvent{
		EventID:    "approval:" + strconv.FormatUint(uint64(instance.ID), 10) + ":submission",
		InstanceID: instance.ID, BusinessType: instance.BusinessType, BusinessID: instance.BusinessID,
		SubmitterAccountID: instance.SubmitterAccountID, Provider: instance.Provider,
		CorrelationID: instance.CorrelationID, OccurredAt: instance.CreatedAt,
	}
	if err := s.eventWriter.Append(ctx, tx, event); err != nil {
		return Reference{}, err
	}
	return Reference{InstanceID: instance.ID, Status: instance.Status}, nil
}

func (s *CreationService) validatePreparation(request CreateRequest, now time.Time) error {
	preparation := request.Preparation
	if preparation.issuer != s || preparation.expiresAt.IsZero() || !preparation.expiresAt.After(now) {
		return errors.New(errors.CodeServiceUnavailable, "审批可用性检查已失效，请重新提交")
	}
	if request.BusinessType != preparation.businessType ||
		request.SubmitterAccountID != preparation.submitterAccountID ||
		request.CorrelationID != preparation.correlationID {
		return errors.New(errors.CodeInvalidParam, "审批准备结果与业务申请不匹配")
	}
	return nil
}

// UnavailableProviderPort 在当前环境未装配有效审批 Adapter 时失败关闭。
type UnavailableProviderPort struct{}

// Prepare 拒绝在缺少有效 Adapter、场景或发起身份时创建业务审批。
func (UnavailableProviderPort) Prepare(_ context.Context, _ PrepareRequest) (ProviderPreparation, error) {
	return ProviderPreparation{}, errors.New(errors.CodeServiceUnavailable, "当前环境没有可用审批渠道")
}

// CreateContextInTx 防止未配置渠道上下文时误写审批事实。
func (UnavailableProviderPort) CreateContextInTx(_ context.Context, _ *gorm.DB, _ ProviderPreparation, _ uint) error {
	return errors.New(errors.CodeServiceUnavailable, "当前环境没有可用审批渠道")
}
