package approval

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

// ReconcileApprovedDecisionAsStale closes an approved generic Approval as
// cancelled when its owning domain reports that the frozen subject changed.
// The exact approved decision receipt fences this narrow compensation path.
func (store *Store) ReconcileApprovedDecisionAsStale(ctx context.Context, params ApprovedStaleParams) (DecisionResult, error) {
	if err := store.Validate(); err != nil {
		return DecisionResult{}, err
	}
	params.WorkspaceID = strings.TrimSpace(params.WorkspaceID)
	params.ApprovalID = strings.TrimSpace(params.ApprovalID)
	params.SubjectType = strings.TrimSpace(params.SubjectType)
	params.ActorMemberID = strings.TrimSpace(params.ActorMemberID)
	params.ClientDecisionID = strings.TrimSpace(params.ClientDecisionID)
	params.Reason = strings.TrimSpace(params.Reason)
	if params.WorkspaceID == "" || params.ApprovalID == "" || params.SubjectType == "" ||
		params.ActorMemberID == "" || params.ClientDecisionID == "" || params.ApprovedVersion <= 1 || params.Reason == "" {
		return DecisionResult{}, storeerr.BadRequest("stale Approval reconciliation identities, approved version, and reason are required")
	}
	if params.Now.IsZero() {
		params.Now = time.Now().UTC()
	} else {
		params.Now = params.Now.UTC()
	}
	var result DecisionResult
	err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		instance, err := loadInstance(ctx, tx, params.WorkspaceID, params.ApprovalID, true)
		if err != nil {
			return err
		}
		receipt, found, err := loadDecisionReceipt(ctx, tx, instance.ID, params.ActorMemberID, params.ClientDecisionID)
		if err != nil {
			return err
		}
		if !found || receipt.Decision != DecisionApproved || receipt.ResultVersion != params.ApprovedVersion ||
			instance.SubjectType != params.SubjectType {
			return storeerr.Conflict("stale Approval reconciliation does not match the approved decision receipt")
		}
		assignment, err := loadAssignment(ctx, tx, params.WorkspaceID, receipt.AssignmentID, true)
		if err != nil {
			return err
		}
		if instance.State == StateCancelled && instance.Decision == DecisionCancelled &&
			instance.DecisionReason == params.Reason && instance.Version == params.ApprovedVersion+1 &&
			assignment.Status == AssignmentCancelled {
			result = DecisionResult{Instance: instance, Assignment: assignment, Receipt: receipt, AlreadyApplied: true}
			return nil
		}
		if instance.State != StateApproved || instance.Decision != DecisionApproved || instance.Version != params.ApprovedVersion ||
			assignment.Status != AssignmentApproved || assignment.ApproverMemberID != params.ActorMemberID {
			return storeerr.Conflict("only the exact approved Approval may be reconciled as stale")
		}
		updated, err := tx.Exec(ctx, `
UPDATE approval_instances
SET state='cancelled', decision='cancelled', decision_reason=$4,
    version=version+1, updated_at=$5
WHERE workspace_id=$1 AND id=$2 AND version=$3 AND state='approved' AND decision='approved'
`, params.WorkspaceID, params.ApprovalID, params.ApprovedVersion, params.Reason, params.Now)
		if err != nil {
			return fmt.Errorf("reconcile stale approval instance: %w", err)
		}
		if updated.RowsAffected() != 1 {
			return storeerr.Conflict("approval version changed during stale reconciliation")
		}
		updated, err = tx.Exec(ctx, `
UPDATE approval_assignments
SET status='cancelled', version=version+1, updated_at=$4
WHERE workspace_id=$1 AND id=$2 AND approval_instance_id=$3 AND status='approved'
`, params.WorkspaceID, assignment.ID, params.ApprovalID, params.Now)
		if err != nil {
			return fmt.Errorf("reconcile stale approval assignment: %w", err)
		}
		if updated.RowsAffected() != 1 {
			return storeerr.Conflict("approval assignment changed during stale reconciliation")
		}
		result.Instance, err = loadInstance(ctx, tx, params.WorkspaceID, params.ApprovalID, false)
		if err != nil {
			return err
		}
		result.Assignment, err = loadAssignment(ctx, tx, params.WorkspaceID, assignment.ID, false)
		result.Receipt = receipt
		return err
	})
	return result, err
}
