package approvalapp

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

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

// workflowRunApprovalSnapshot is reconstructed from the frozen effect and Run
// rows. Mutable Plan data is deliberately not consulted for recipient/action
// authority.
type workflowRunApprovalSnapshot struct {
	approval     orchestrator.ApprovalRequest
	effectStatus string
	runStatus    string
	memberType   string
	memberStatus string
	version      int64
}

func resolveWorkflowRunAssigneeFact(
	ctx context.Context,
	queryer coredb.QueryRower,
	request app.AppNotificationRecipientFactRequest,
) (app.AppNotificationRecipientFact, error) {
	if strings.TrimSpace(request.Basis.Kind) != RecipientBasisWorkflowRunAssignee {
		return app.AppNotificationRecipientFact{}, storeerr.NotFound("Workflow Run approval recipient fact not found")
	}
	snapshot, err := loadWorkflowRunApprovalSnapshot(ctx, queryer, request.WorkspaceID, request.Basis.Ref)
	if err != nil {
		return app.AppNotificationRecipientFact{}, err
	}
	if err := validateWorkflowRunBusinessObject(request.BusinessObject, snapshot); err != nil {
		return app.AppNotificationRecipientFact{}, err
	}
	expectedMemberID := strings.TrimSpace(request.ExpectedMemberID)
	if expectedMemberID != "" && expectedMemberID != snapshot.approval.AssigneeMemberID {
		return app.AppNotificationRecipientFact{}, storeerr.Forbidden("Workflow Run approval assignee does not match")
	}
	active := snapshot.version == 1 && workflowRunActiveStatus(snapshot.runStatus) &&
		workflowRunActiveEffectStatus(snapshot.effectStatus) && snapshot.memberType == "human" && snapshot.memberStatus == "active"
	return app.AppNotificationRecipientFact{
		Kind: RecipientBasisWorkflowRunAssignee, Ref: snapshot.approval.EffectID,
		WorkspaceID: snapshot.approval.WorkspaceID, RecipientMemberID: snapshot.approval.AssigneeMemberID,
		Version: snapshot.version, Status: snapshot.effectStatus, Active: active,
		PermissionBasisRef: snapshot.approval.EffectID,
	}, nil
}

func loadWorkflowRunApprovalSnapshot(ctx context.Context, queryer coredb.QueryRower, workspaceID, effectID string) (workflowRunApprovalSnapshot, error) {
	workspaceID, effectID = strings.TrimSpace(workspaceID), strings.TrimSpace(effectID)
	if workspaceID == "" || effectID == "" {
		return workflowRunApprovalSnapshot{}, storeerr.NotFound("Workflow Run approval effect not found")
	}
	var (
		effectWorkspaceID, runID, activationID, effectKind, effectStatus string
		requestJSON                                                      []byte
		frozenAssigneeMemberID                                           sql.NullString
		planID, planVersionID, runStatus, authorMemberID                 string
		activationLogicalKey, activationNodeID                           string
		memberType, memberStatus                                         string
	)
	err := queryer.QueryRow(ctx, `
SELECT effect.workspace_id,effect.run_id,effect.activation_id,
       effect.effect_kind,effect.status,effect.request_json,
       effect.approval_assignee_member_id,
       run.plan_id,run.plan_version_id,run.status,run.author_member_id_snapshot,
       activation.logical_key,activation.node_id,
       COALESCE(member.type,''),COALESCE(member.status,'')
FROM workflow_run_effects effect
JOIN workflow_run_instances run
  ON run.workspace_id=effect.workspace_id AND run.id=effect.run_id
JOIN workflow_run_activations activation
  ON activation.workspace_id=effect.workspace_id
 AND activation.run_id=effect.run_id AND activation.id=effect.activation_id
LEFT JOIN members member
  ON member.workspace_id=effect.workspace_id
 AND member.id=effect.approval_assignee_member_id
WHERE effect.workspace_id=$1 AND effect.id=$2
LIMIT 1`, workspaceID, effectID).Scan(
		&effectWorkspaceID, &runID, &activationID,
		&effectKind, &effectStatus, &requestJSON,
		&frozenAssigneeMemberID,
		&planID, &planVersionID, &runStatus, &authorMemberID,
		&activationLogicalKey, &activationNodeID,
		&memberType, &memberStatus,
	)
	if err != nil {
		if coredb.IsNotFound(err) {
			return workflowRunApprovalSnapshot{}, storeerr.NotFound("Workflow Run approval effect not found")
		}
		return workflowRunApprovalSnapshot{}, fmt.Errorf("resolve Workflow Run approval recipient: %w", err)
	}
	effectRequest, err := orchestrator.DecodeEffectRequest(requestJSON)
	if err != nil {
		return workflowRunApprovalSnapshot{}, fmt.Errorf("decode Workflow Run approval effect: %w", err)
	}
	if effectKind != orchestrator.EffectKindApproval || string(effectRequest.Intent.Kind) != orchestrator.EffectKindApproval {
		return workflowRunApprovalSnapshot{}, storeerr.NotFound("Workflow Run approval effect not found")
	}
	if !frozenAssigneeMemberID.Valid || strings.TrimSpace(frozenAssigneeMemberID.String) == "" {
		return workflowRunApprovalSnapshot{}, fmt.Errorf("Workflow Run approval effect has no frozen assignee member")
	}
	if effectWorkspaceID != workspaceID || runID == "" || activationID == "" ||
		effectRequest.Intent.ActivationID != activationLogicalKey || effectRequest.Intent.NodeID != activationNodeID {
		return workflowRunApprovalSnapshot{}, fmt.Errorf("Workflow Run approval frozen effect snapshot is inconsistent")
	}
	approval := orchestrator.ApprovalRequest{
		WorkspaceID: effectWorkspaceID, PlanID: planID, PlanVersionID: planVersionID,
		RunID: runID, ActivationID: effectRequest.Intent.ActivationID, EffectID: effectID,
		NodeID: activationNodeID, AssigneeMemberID: frozenAssigneeMemberID.String,
		Summary: effectRequest.Intent.Summary, FeedbackScope: effectRequest.Intent.FeedbackScope,
		CausedByMemberID: authorMemberID,
	}.Normalize()
	if err := approval.Validate(); err != nil {
		return workflowRunApprovalSnapshot{}, fmt.Errorf("validate Workflow Run approval effect: %w", err)
	}
	version := int64(1)
	if workflowRunTerminalStatus(runStatus) || workflowRunTerminalEffectStatus(effectStatus) {
		version = 2
	}
	return workflowRunApprovalSnapshot{
		approval: approval, effectStatus: strings.TrimSpace(effectStatus), runStatus: strings.TrimSpace(runStatus),
		memberType: strings.TrimSpace(memberType), memberStatus: strings.TrimSpace(memberStatus), version: version,
	}, nil
}

func validateWorkflowRunBusinessObject(object app.AppNotificationBusinessObject, snapshot workflowRunApprovalSnapshot) error {
	if strings.TrimSpace(object.Namespace) != BusinessNamespace ||
		strings.TrimSpace(object.Type) != BusinessTypeWorkflowRunApproval ||
		strings.TrimSpace(object.ID) != snapshot.approval.EffectID {
		return storeerr.Forbidden("Workflow Run approval business object does not match recipient fact")
	}
	if object.Version <= 0 || object.Version > snapshot.version {
		return storeerr.Conflict("Workflow Run approval business object version is invalid")
	}
	return nil
}

func workflowRunTerminalStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case "succeeded", "failed", "canceled":
		return true
	default:
		return false
	}
}

func workflowRunActiveStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case "running", "waiting":
		return true
	default:
		return false
	}
}

func workflowRunActiveEffectStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case "dispatching", "delivered", "awaiting":
		return true
	default:
		return false
	}
}

func workflowRunTerminalEffectStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case "completed", "failed", "canceled":
		return true
	default:
		return false
	}
}
