package plan

import (
	"bytes"
	"context"
	"fmt"

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

// SaveDraft updates one draft inside the caller-owned transaction. Keeping the
// CAS and draft mutation in the Team Workflow transaction is required for
// direct Team publication atomicity.
func (store *TxStore) SaveDraft(ctx context.Context, params SaveDraftParams) (PlanDraft, error) {
	if err := store.validate(); err != nil {
		return PlanDraft{}, err
	}
	params.WorkspaceID, params.PlanID = trim(params.WorkspaceID), trim(params.PlanID)
	if params.WorkspaceID == "" || params.PlanID == "" || params.ExpectedRevision <= 0 {
		return PlanDraft{}, storeerr.BadRequest("workspaceId, planId, and a positive expectedRevision are required")
	}
	actor, err := validateActor(params.Actor)
	if err != nil {
		return PlanDraft{}, err
	}
	plan, err := loadPlan(ctx, store.tx, params.WorkspaceID, params.PlanID, true)
	if err != nil {
		return PlanDraft{}, err
	}
	if plan.ArchivedAt != nil {
		return PlanDraft{}, ErrArchived
	}
	draft, err := loadDraft(ctx, store.tx, params.WorkspaceID, params.PlanID, true)
	if err != nil {
		return PlanDraft{}, err
	}
	if draft.Revision != params.ExpectedRevision {
		return PlanDraft{}, RevisionConflictError{Resource: "plan draft", Expected: params.ExpectedRevision, Actual: draft.Revision}
	}
	layout := params.Layout
	if params.PreserveLayout {
		layout = draft.Layout
	}
	if draft.Source == params.Source && bytes.Equal(draft.Layout, layout) {
		return draft, nil
	}
	result, err := store.tx.Exec(ctx, `
UPDATE workflow_plan_drafts
SET source = $1, layout = $2, revision = revision + 1,
    updated_by_member_id = $3, updated_by_actor_type = $4,
    updated_by_actor_id = $5, updated_at = $6
WHERE workspace_id = $7 AND plan_id = $8 AND revision = $9
`, params.Source, string(layout), actor.MemberID, actor.Type, actor.ID,
		utcOrNow(params.Now), params.WorkspaceID, params.PlanID, params.ExpectedRevision)
	if err != nil {
		return PlanDraft{}, fmt.Errorf("save workflow plan draft in transaction: %w", mapWriteError(err))
	}
	if result.RowsAffected() == 0 {
		return PlanDraft{}, RevisionConflictError{Resource: "plan draft", Expected: params.ExpectedRevision, Actual: draft.Revision}
	}
	return loadDraft(ctx, store.tx, params.WorkspaceID, params.PlanID, false)
}
