package approvalapp

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/kbgovernance"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/approval"
)

const maxWikiGenerationNotificationAssignments = 100

// PresentPending projects the frozen Wiki request only to its exact reviewers
// and applicant. The deterministic client IDs make request replays harmless.
func (handler *WikiGenerationSubjectHandler) PresentPending(ctx context.Context, result SubmitResult) (app.AppNotificationResult, error) {
	if err := handler.ensureWikiGenerationProjector(); err != nil {
		return app.AppNotificationResult{}, err
	}
	instance, payload, err := wikiGenerationNotificationPayload(result.Instance, approval.StatePending)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	if len(result.Assignments) == 0 || len(result.Assignments) > maxWikiGenerationNotificationAssignments {
		return app.AppNotificationResult{}, fmt.Errorf("Wiki generation Approval assignments are invalid")
	}
	if _, err := handler.projector.provisioner.EnsureWorkspace(ctx, instance.WorkspaceID, time.Now().UTC()); err != nil {
		return app.AppNotificationResult{}, err
	}
	for _, assignment := range result.Assignments {
		if err := validateKBGovernanceAssignment("Wiki generation", instance, assignment, approval.AssignmentPending); err != nil {
			return app.AppNotificationResult{}, err
		}
		content, err := wikiGenerationNotificationContent(instance, payload, &assignment, false)
		if err != nil {
			return app.AppNotificationResult{}, err
		}
		if _, err := handler.projector.notifications.CreateAppNotificationForBuiltin(ctx, IdentityID, instance.WorkspaceID, app.AppNotificationCommand{
			ClientNotificationID: wikiGenerationReviewerClientNotificationID(instance.ID, assignment.ApproverMemberID),
			Target:               app.AppNotificationTarget{Kind: app.AppNotificationTargetHumanDM, MemberID: assignment.ApproverMemberID},
			Content:              content,
			BusinessObject:       kbApprovalNotificationBusinessObject(instance, wikiGenerationURL(payload.KnowledgeBaseID, payload.SourceID)),
			RecipientBasis:       app.AppNotificationRecipientBasis{Kind: RecipientBasisAssignment, Ref: assignment.ID},
			CausedByActor:        app.AppNotificationActorRef{Type: "human", ID: instance.ApplicantMemberID},
			CorrelationID:        instance.CorrelationID,
		}); err != nil {
			return app.AppNotificationResult{}, err
		}
	}
	content, err := wikiGenerationNotificationContent(instance, payload, nil, true)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	return handler.projector.notifications.CreateAppNotificationForBuiltin(ctx, IdentityID, instance.WorkspaceID, app.AppNotificationCommand{
		ClientNotificationID: wikiGenerationApplicantPendingClientNotificationID(instance.ID),
		Target:               app.AppNotificationTarget{Kind: app.AppNotificationTargetHumanDM, MemberID: instance.ApplicantMemberID},
		Content:              content,
		BusinessObject:       kbApprovalNotificationBusinessObject(instance, wikiGenerationURL(payload.KnowledgeBaseID, payload.SourceID)),
		RecipientBasis:       app.AppNotificationRecipientBasis{Kind: RecipientBasisApplicant, Ref: instance.ID},
		CausedByActor:        app.AppNotificationActorRef{Type: "human", ID: instance.ApplicantMemberID},
		CorrelationID:        instance.CorrelationID,
	})
}

// PresentResolved converges all pending cards and sends the applicant a
// separate read-only decision card. Approval means queued, not generated.
func (handler *WikiGenerationSubjectHandler) PresentResolved(ctx context.Context, result DecisionResult, presentation PresentationContext) (app.AppNotificationResult, error) {
	if err := handler.ensureWikiGenerationProjector(); err != nil {
		return app.AppNotificationResult{}, err
	}
	instance, payload, err := wikiGenerationNotificationPayload(result.Instance, "")
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	if instance.State != approval.StateApproved && instance.State != approval.StateRejected && instance.State != approval.StateCancelled {
		return app.AppNotificationResult{}, fmt.Errorf("Wiki generation Approval is not terminal")
	}
	assignments, err := handler.approvals.ListAssignments(ctx, instance.WorkspaceID, instance.ID)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	if len(assignments) == 0 || len(assignments) > maxWikiGenerationNotificationAssignments {
		return app.AppNotificationResult{}, fmt.Errorf("Wiki generation Approval assignments are invalid")
	}
	provisioned, err := handler.projector.provisioner.EnsureWorkspace(ctx, instance.WorkspaceID, time.Now().UTC())
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	var projected app.AppNotificationResult
	var actionProjection *app.AppNotificationResult
	for _, assignment := range assignments {
		if err := validateKBGovernanceAssignment("Wiki generation", instance, assignment, kbApprovalTerminalAssignmentStatus(instance, assignment)); err != nil {
			return app.AppNotificationResult{}, err
		}
		content, err := wikiGenerationNotificationContent(instance, payload, &assignment, false)
		if err != nil {
			return app.AppNotificationResult{}, err
		}
		resolved, found, err := resolveKBGovernanceNotification(
			ctx, handler.projector, provisioned.Installation.ID,
			wikiGenerationReviewerClientNotificationID(instance.ID, assignment.ApproverMemberID),
			content, instance, "wiki-generation-review-terminal:"+instance.ID+":"+assignment.ApproverMemberID,
			presentation, "Wiki generation",
		)
		if err != nil {
			return app.AppNotificationResult{}, err
		}
		if found {
			projected = resolved
			if resolved.NotificationID == strings.TrimSpace(presentation.NotificationID) {
				actionCopy := resolved
				actionProjection = &actionCopy
			}
		}
	}
	terminalContent, err := wikiGenerationNotificationContent(instance, payload, nil, true)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	if resolved, found, err := resolveKBGovernanceNotification(
		ctx, handler.projector, provisioned.Installation.ID,
		wikiGenerationApplicantPendingClientNotificationID(instance.ID), terminalContent, instance,
		"wiki-generation-applicant-pending-terminal:"+instance.ID, PresentationContext{}, "Wiki generation",
	); err != nil {
		return app.AppNotificationResult{}, err
	} else if found {
		projected = resolved
	}
	active, err := handler.projector.activeHumanMember(ctx, instance.WorkspaceID, instance.ApplicantMemberID)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	if active {
		created, err := handler.projector.notifications.CreateAppNotificationForBuiltin(ctx, IdentityID, instance.WorkspaceID, app.AppNotificationCommand{
			ClientNotificationID: wikiGenerationApplicantResultClientNotificationID(instance.ID),
			Target:               app.AppNotificationTarget{Kind: app.AppNotificationTargetHumanDM, MemberID: instance.ApplicantMemberID},
			Content:              terminalContent,
			BusinessObject:       kbApprovalNotificationBusinessObject(instance, wikiGenerationURL(payload.KnowledgeBaseID, payload.SourceID)),
			RecipientBasis:       app.AppNotificationRecipientBasis{Kind: RecipientBasisApplicant, Ref: instance.ID},
			CausedByActor:        kbApprovalTerminalActor(instance),
			CorrelationID:        instance.CorrelationID,
		})
		if err != nil {
			return app.AppNotificationResult{}, err
		}
		projected = created
	}
	if actionProjection != nil {
		return *actionProjection, nil
	}
	if projected.NotificationID == "" {
		projected = app.AppNotificationResult{WorkspaceID: instance.WorkspaceID, AlreadyCreated: true, Status: app.AppNotificationStatusResolved}
	}
	return projected, nil
}

func (handler *WikiGenerationSubjectHandler) ensureWikiGenerationProjector() error {
	if handler == nil || handler.approvals == nil || handler.projector == nil || handler.projector.notifications == nil || handler.projector.provisioner == nil {
		return fmt.Errorf("Wiki generation Approval projector is not configured")
	}
	return nil
}

func wikiGenerationNotificationPayload(instance approval.Instance, expectedState string) (approval.Instance, kbgovernance.WikiGenerationPayload, error) {
	if instance.SubjectType != kbgovernance.WikiGenerationSubjectType || instance.ID == "" || instance.Version <= 0 ||
		(expectedState != "" && instance.State != expectedState) {
		return approval.Instance{}, kbgovernance.WikiGenerationPayload{}, fmt.Errorf("Wiki generation Approval notification subject is invalid")
	}
	var payload kbgovernance.WikiGenerationPayload
	if err := json.Unmarshal(instance.Payload, &payload); err != nil || payload.SchemaVersion != kbgovernance.WikiGenerationPayloadSchema ||
		strings.TrimSpace(payload.KnowledgeBaseID) == "" || strings.TrimSpace(payload.SourceID) == "" || strings.TrimSpace(payload.SourceRevision) == "" {
		return approval.Instance{}, kbgovernance.WikiGenerationPayload{}, fmt.Errorf("Wiki generation Approval notification payload is invalid")
	}
	return instance, payload, nil
}

func wikiGenerationNotificationContent(instance approval.Instance, payload kbgovernance.WikiGenerationPayload, assignment *approval.Assignment, applicant bool) (app.AppNotificationContent, error) {
	title, titleKey := wikiGenerationNotificationTitle(instance, applicant)
	template := "approval_action"
	terminalApplicant := applicant && instance.State != approval.StatePending
	if applicant {
		template = "default"
		if terminalApplicant {
			template = "approval_result"
		}
	}
	actions := []map[string]any{}
	if !applicant || terminalApplicant {
		actions = append(actions, map[string]any{
			"actionKey": "open_wiki_setup", "type": "open_url", "label": "打开 Wiki 设置",
			"semantics": ActionSemanticsNavigate, "url": wikiGenerationURL(payload.KnowledgeBaseID, payload.SourceID),
		})
	}
	card := map[string]any{
		"schemaVersion": "1", "template": template, "title": title, "titleKey": titleKey,
		"status": instance.State,
		"fields": []map[string]any{
			{"label": "knowledge_base_id", "value": payload.KnowledgeBaseID},
			{"label": "source_id", "value": payload.SourceID},
			{"label": "source_revision", "value": payload.SourceRevision},
			{"label": "scope", "value": payload.Scope},
			{"label": "language", "value": payload.Language},
			{"label": "reason", "value": payload.Reason},
		},
		"approval": map[string]any{
			"id": instance.ID, "version": instance.Version, "state": instance.State,
			"reason": instance.DecisionReason,
		},
		"actions": actions,
	}
	if instance.State == approval.StatePending && assignment != nil && !applicant {
		actorPolicy := map[string]any{"kind": "members", "memberIds": []string{assignment.ApproverMemberID}}
		card["actions"] = append([]map[string]any{
			{"actionKey": ActionApprove, "type": "callback", "label": "批准并生成", "semantics": ActionSemanticsCommit, "actorPolicy": actorPolicy},
			{
				"actionKey": ActionReject, "type": "callback", "label": "不通过", "semantics": ActionSemanticsRevise,
				"actorPolicy": actorPolicy,
				"inputSchema": map[string]any{
					"type": "object", "properties": map[string]any{"reason": map[string]any{"type": "string", "maxLength": 1000}},
					"required": []string{"reason"}, "additionalProperties": false,
				},
			},
		}, card["actions"].([]map[string]any)...)
	}
	encoded, err := json.Marshal(card)
	if err != nil {
		return app.AppNotificationContent{}, fmt.Errorf("marshal Wiki generation Approval notification: %w", err)
	}
	return app.AppNotificationContent{Kind: app.AppNotificationContentInteractiveCard, FallbackText: title, CardJSON: encoded}, nil
}

func wikiGenerationNotificationTitle(instance approval.Instance, applicant bool) (string, string) {
	if instance.State == approval.StatePending {
		if applicant {
			return "Wiki 生成申请待审批", "kb_wiki_generation_applicant_pending"
		}
		return "审批 Wiki 生成申请", "kb_wiki_generation_review"
	}
	switch {
	case instance.State == approval.StateApproved:
		return "Wiki 生成申请已批准并排队", "kb_wiki_generation_queued"
	case instance.State == approval.StateRejected:
		return "Wiki 生成申请未通过", "kb_wiki_generation_rejected"
	case instance.DecisionReason == "stale_subject":
		return "Wiki 生成申请已失效", "kb_wiki_generation_stale"
	default:
		return "Wiki 生成申请已撤回", "kb_wiki_generation_withdrawn"
	}
}

func wikiGenerationURL(kbID, sourceID string) string {
	return "/kb/" + url.PathEscape(strings.TrimSpace(kbID)) + "?setup=wiki&source=" + url.QueryEscape(strings.TrimSpace(sourceID))
}

func wikiGenerationReviewerClientNotificationID(approvalID, memberID string) string {
	return "kb-wiki-generation:" + strings.TrimSpace(approvalID) + ":reviewer:" + strings.TrimSpace(memberID)
}

func wikiGenerationApplicantPendingClientNotificationID(approvalID string) string {
	return "kb-wiki-generation:" + strings.TrimSpace(approvalID) + ":applicant:pending"
}

func wikiGenerationApplicantResultClientNotificationID(approvalID string) string {
	return "kb-wiki-generation:" + strings.TrimSpace(approvalID) + ":applicant:result"
}

var (
	_ SubjectPendingPresenter  = (*WikiGenerationSubjectHandler)(nil)
	_ SubjectResolvedPresenter = (*WikiGenerationSubjectHandler)(nil)
)
