package invocation

import (
	"context"
	"fmt"
	"strings"

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

func loadInvocation(ctx context.Context, queryer coredb.QueryRower, workspaceID string, invocationID string) (Invocation, error) {
	query := invocationSelectSQL() + "\nWHERE i.workspace_id = $1 AND i.id = $2"
	value, err := scanInvocation(queryer.QueryRow(ctx, query, workspaceID, invocationID))
	if err != nil {
		return Invocation{}, mapReadError("load workflow invocation", err)
	}
	return value, nil
}

func loadInvocationByActorIdempotency(ctx context.Context, queryer coredb.QueryRower, workspaceID string, actor Actor, key string) (Invocation, bool, error) {
	query := invocationSelectSQL() + `
WHERE i.workspace_id = $1
  AND i.invoked_by_actor_type = $2
  AND i.invoked_by_actor_id = $3
  AND i.idempotency_key = $4`
	value, err := scanInvocation(queryer.QueryRow(ctx, query, workspaceID, actor.Type, actor.ID, key))
	if coredb.IsNotFound(err) {
		return Invocation{}, false, nil
	}
	if err != nil {
		return Invocation{}, false, fmt.Errorf("load workflow invocation idempotency record: %w", err)
	}
	return value, true, nil
}

func lockInvocationByActorIdempotency(ctx context.Context, queryer coredb.QueryRower, workspaceID string, actor Actor, key string) (Invocation, bool, error) {
	value, err := scanLockedInvocation(queryer.QueryRow(ctx, invocationLockSelectSQL()+`
WHERE i.workspace_id = $1
  AND i.invoked_by_actor_type = $2
  AND i.invoked_by_actor_id = $3
  AND i.idempotency_key = $4
FOR UPDATE`, workspaceID, actor.Type, actor.ID, key))
	if coredb.IsNotFound(err) {
		return Invocation{}, false, nil
	}
	if err != nil {
		return Invocation{}, false, fmt.Errorf("lock workflow invocation idempotency record: %w", err)
	}
	return value, true, nil
}

func lockInvocationByOccurrence(ctx context.Context, queryer coredb.QueryRower, workspaceID string, sourceID string, ref string) (Invocation, bool, error) {
	value, err := scanLockedInvocation(queryer.QueryRow(ctx, invocationLockSelectSQL()+`
WHERE i.workspace_id = $1
  AND i.binding_source_id = $2
  AND i.source_occurrence_ref = $3
FOR UPDATE`, workspaceID, sourceID, ref))
	if coredb.IsNotFound(err) {
		return Invocation{}, false, nil
	}
	if err != nil {
		return Invocation{}, false, fmt.Errorf("lock workflow invocation occurrence: %w", err)
	}
	return value, true, nil
}

func loadRunByInvocation(ctx context.Context, queryer coredb.QueryRower, workspaceID string, invocationID string) (Run, error) {
	value, err := scanRun(queryer.QueryRow(ctx, runSelectSQL()+`
WHERE workspace_id = $1 AND invocation_id = $2
`, workspaceID, invocationID))
	if err != nil {
		return Run{}, mapReadError("load workflow Run by Invocation", err)
	}
	return value, nil
}

func lockRunByInvocation(ctx context.Context, queryer coredb.QueryRower, workspaceID string, invocationID string) (Run, error) {
	value, err := scanRun(queryer.QueryRow(ctx, runSelectSQL()+`
WHERE workspace_id = $1 AND invocation_id = $2
FOR UPDATE
`, workspaceID, invocationID))
	if err != nil {
		return Run{}, mapReadError("lock workflow Run by Invocation", err)
	}
	return value, nil
}

func loadAcceptedRunEvent(ctx context.Context, queryer coredb.QueryRower, workspaceID string, runID string) (RunEvent, error) {
	value, err := scanRunEvent(queryer.QueryRow(ctx, runEventSelectSQL()+`
WHERE workspace_id = $1 AND run_id = $2 AND seq = 1 AND event_type = $3
`, workspaceID, runID, domain.RunEventAccepted))
	if err != nil {
		return RunEvent{}, mapReadError("load accepted workflow Run event", err)
	}
	return value, nil
}

func lockAcceptedRunEvent(ctx context.Context, queryer coredb.QueryRower, workspaceID string, runID string) (RunEvent, error) {
	value, err := scanRunEvent(queryer.QueryRow(ctx, runEventSelectSQL()+`
WHERE workspace_id = $1 AND run_id = $2 AND seq = 1 AND event_type = $3
FOR UPDATE
`, workspaceID, runID, domain.RunEventAccepted))
	if err != nil {
		return RunEvent{}, mapReadError("lock accepted workflow Run event", err)
	}
	return value, nil
}

func (store *Store) LoadInvocation(ctx context.Context, workspaceID string, invocationID string) (Invocation, error) {
	if err := store.Validate(); err != nil {
		return Invocation{}, err
	}
	workspaceID, invocationID = strings.TrimSpace(workspaceID), strings.TrimSpace(invocationID)
	if workspaceID == "" || invocationID == "" {
		return Invocation{}, storeerr.BadRequest("workspaceId and invocationId are required")
	}
	return loadInvocation(ctx, store.db, workspaceID, invocationID)
}

func (store *Store) LoadRun(ctx context.Context, workspaceID string, runID string) (Run, error) {
	if err := store.Validate(); err != nil {
		return Run{}, err
	}
	workspaceID, runID = strings.TrimSpace(workspaceID), strings.TrimSpace(runID)
	if workspaceID == "" || runID == "" {
		return Run{}, storeerr.BadRequest("workspaceId and runId are required")
	}
	value, err := scanRun(store.db.QueryRow(ctx, runSelectSQL()+`
WHERE workspace_id = $1 AND id = $2
`, workspaceID, runID))
	if err != nil {
		return Run{}, mapReadError("load workflow Run", err)
	}
	return value, nil
}

func (store *Store) LoadRunByInvocation(ctx context.Context, workspaceID string, invocationID string) (Run, error) {
	if err := store.Validate(); err != nil {
		return Run{}, err
	}
	workspaceID, invocationID = strings.TrimSpace(workspaceID), strings.TrimSpace(invocationID)
	if workspaceID == "" || invocationID == "" {
		return Run{}, storeerr.BadRequest("workspaceId and invocationId are required")
	}
	return loadRunByInvocation(ctx, store.db, workspaceID, invocationID)
}
