package approval

import (
	"context"
	"time"

	"gorm.io/gorm"

	approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

// DecisionDeliveryStore 持久化标准决策消费事实并管理处理租约。
type DecisionDeliveryStore struct {
	db *gorm.DB
}

// NewDecisionDeliveryStore 创建标准决策投递 Store。
func NewDecisionDeliveryStore(db *gorm.DB) *DecisionDeliveryStore {
	return &DecisionDeliveryStore{db: db}
}

// Create 在审批状态事务中创建唯一标准决策投递事实。
func (s *DecisionDeliveryStore) Create(ctx context.Context, tx *gorm.DB, event approvalapp.TerminalDecisionEvent) error {
	if tx == nil {
		return errors.New(errors.CodeInternalError, "审批决策投递必须使用审批状态事务")
	}
	record := model.ApprovalDecisionDelivery{
		InstanceID: event.InstanceID, Decision: event.Decision, EventID: event.EventID,
		Status: constants.ApprovalDecisionDeliveryPending, CreatedAt: event.OccurredAt, UpdatedAt: event.OccurredAt,
	}
	if err := tx.WithContext(ctx).Create(&record).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建审批决策投递事实失败")
	}
	return nil
}

// Claim 领取待处理、失败或租约已过期的标准决策。
func (s *DecisionDeliveryStore) Claim(
	ctx context.Context,
	eventID string,
	owner string,
	now time.Time,
	duration time.Duration,
) (bool, error) {
	if s == nil || s.db == nil || eventID == "" || owner == "" || duration <= 0 {
		return false, errors.New(errors.CodeInternalError, "审批决策处理租约 Store 未完整配置")
	}
	result := s.db.WithContext(ctx).Model(&model.ApprovalDecisionDelivery{}).
		Where("event_id = ? AND (status IN ? OR (status = ? AND lease_expires_at <= ?))",
			eventID,
			[]int{constants.ApprovalDecisionDeliveryPending, constants.ApprovalDecisionDeliveryFailed},
			constants.ApprovalDecisionDeliveryProcessing, now).
		Updates(map[string]any{
			"status":      constants.ApprovalDecisionDeliveryProcessing,
			"lease_owner": owner, "lease_expires_at": now.Add(duration), "updated_at": now,
		})
	if result.Error != nil {
		return false, errors.Wrap(errors.CodeDatabaseError, result.Error, "领取审批决策处理租约失败")
	}
	return result.RowsAffected == 1, nil
}

// MarkSucceeded 标记当前租约的业务消费者已幂等处理成功。
func (s *DecisionDeliveryStore) MarkSucceeded(ctx context.Context, eventID string, owner string, now time.Time) (bool, error) {
	if s == nil || s.db == nil {
		return false, errors.New(errors.CodeInternalError, "审批决策处理租约 Store 未配置")
	}
	result := s.db.WithContext(ctx).Model(&model.ApprovalDecisionDelivery{}).
		Where("event_id = ? AND status = ? AND lease_owner = ?", eventID, constants.ApprovalDecisionDeliveryProcessing, owner).
		Updates(map[string]any{
			"status": constants.ApprovalDecisionDeliverySucceeded, "processed_at": now,
			"lease_owner": nil, "lease_expires_at": nil, "last_error": "", "updated_at": now,
		})
	if result.Error != nil {
		return false, errors.Wrap(errors.CodeDatabaseError, result.Error, "完成审批决策业务处理失败")
	}
	return result.RowsAffected == 1, nil
}

// MarkFailed 记录当前租约的安全失败摘要并释放租约等待重试。
func (s *DecisionDeliveryStore) MarkFailed(
	ctx context.Context,
	eventID string,
	owner string,
	now time.Time,
	errorSummary string,
) (bool, error) {
	if s == nil || s.db == nil {
		return false, errors.New(errors.CodeInternalError, "审批决策处理租约 Store 未配置")
	}
	result := s.db.WithContext(ctx).Model(&model.ApprovalDecisionDelivery{}).
		Where("event_id = ? AND status = ? AND lease_owner = ?", eventID, constants.ApprovalDecisionDeliveryProcessing, owner).
		Updates(map[string]any{
			"status": constants.ApprovalDecisionDeliveryFailed, "retry_count": gorm.Expr("retry_count + 1"),
			"lease_owner": nil, "lease_expires_at": nil, "last_error": errorSummary, "updated_at": now,
		})
	if result.Error != nil {
		return false, errors.Wrap(errors.CodeDatabaseError, result.Error, "记录审批决策业务处理失败")
	}
	return result.RowsAffected == 1, nil
}
