package store

import (
	"context"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
	"github.com/jackc/pgx/v5"
)

const SystemSettingGatewayTrafficMode = "gateway_traffic_mode"

var (
	acceptanceReleaseSHAPattern = regexp.MustCompile(`^[0-9a-f]{40}$`)
	acceptanceDigestPattern     = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
	capacityConfigHashPattern   = regexp.MustCompile(`^[0-9a-f]{64}$`)
)

var (
	ErrProductionTrafficPaused  = errors.New("production traffic is paused for validation")
	ErrAcceptanceNotAuthorized  = errors.New("acceptance request is not authorized")
	ErrAcceptanceRunNotActive   = errors.New("acceptance run is not active")
	ErrAcceptanceStateConflict  = errors.New("acceptance state changed")
	ErrAcceptancePromotionGates = errors.New("acceptance promotion gates have not passed")
)

type GatewayTrafficMode struct {
	Mode              string    `json:"mode"`
	RunID             string    `json:"runId,omitempty"`
	Revision          int64     `json:"revision"`
	ReleaseSHA        string    `json:"releaseSha,omitempty"`
	APIImageDigest    string    `json:"apiImageDigest,omitempty"`
	WorkerImageDigest string    `json:"workerImageDigest,omitempty"`
	ConfigHash        string    `json:"configHash,omitempty"`
	UpdatedAt         time.Time `json:"updatedAt"`
}

type AcceptanceRun struct {
	ID                string         `json:"id"`
	Status            string         `json:"status"`
	ReleaseSHA        string         `json:"releaseSha"`
	APIImageDigest    string         `json:"apiImageDigest"`
	WorkerImageDigest string         `json:"workerImageDigest"`
	APIKeyID          string         `json:"apiKeyId"`
	UserID            string         `json:"userId"`
	EmulatorBaseURL   string         `json:"emulatorBaseUrl"`
	CallbackURL       string         `json:"callbackUrl,omitempty"`
	CapacityProfile   string         `json:"capacityProfile"`
	Config            map[string]any `json:"config"`
	Report            map[string]any `json:"report"`
	FailureReason     string         `json:"failureReason,omitempty"`
	StartedAt         string         `json:"startedAt,omitempty"`
	FinishedAt        string         `json:"finishedAt,omitempty"`
	PromotedAt        string         `json:"promotedAt,omitempty"`
	CreatedAt         time.Time      `json:"createdAt"`
	UpdatedAt         time.Time      `json:"updatedAt"`
}

type CreateAcceptanceRunInput struct {
	ReleaseSHA        string         `json:"releaseSha"`
	APIImageDigest    string         `json:"apiImageDigest"`
	WorkerImageDigest string         `json:"workerImageDigest"`
	APIKeyID          string         `json:"apiKeyId"`
	UserID            string         `json:"userId"`
	Token             string         `json:"token"`
	EmulatorBaseURL   string         `json:"emulatorBaseUrl"`
	CallbackURL       string         `json:"callbackUrl,omitempty"`
	CapacityProfile   string         `json:"capacityProfile"`
	Config            map[string]any `json:"config,omitempty"`
}

type FinishAcceptanceRunInput struct {
	RunID         string         `json:"-"`
	Passed        bool           `json:"passed"`
	Report        map[string]any `json:"report"`
	FailureReason string         `json:"failureReason,omitempty"`
}

type PromoteAcceptanceRunInput struct {
	RunID             string                 `json:"-"`
	Revision          int64                  `json:"revision"`
	ReleaseSHA        string                 `json:"releaseSha"`
	APIImageDigest    string                 `json:"apiImageDigest"`
	WorkerImageDigest string                 `json:"workerImageDigest"`
	ConfigHash        string                 `json:"configHash"`
	CapacityProfiles  []CapacityProfileInput `json:"capacityProfiles"`
}

type PauseGatewayTrafficInput struct {
	Revision          int64  `json:"revision"`
	ReleaseSHA        string `json:"releaseSha"`
	APIImageDigest    string `json:"apiImageDigest"`
	WorkerImageDigest string `json:"workerImageDigest"`
	Reason            string `json:"reason"`
}

func DefaultGatewayTrafficMode() GatewayTrafficMode {
	return GatewayTrafficMode{Mode: "live"}
}

func (s *Store) GetGatewayTrafficMode(ctx context.Context) (GatewayTrafficMode, error) {
	var value []byte
	var updatedAt time.Time
	err := s.pool.QueryRow(ctx, `
SELECT value, updated_at
FROM system_settings
WHERE setting_key = $1`, SystemSettingGatewayTrafficMode).Scan(&value, &updatedAt)
	if err != nil {
		if IsNotFound(err) || IsUndefinedDatabaseObject(err) {
			return DefaultGatewayTrafficMode(), nil
		}
		return GatewayTrafficMode{}, err
	}
	var mode GatewayTrafficMode
	if err := json.Unmarshal(value, &mode); err != nil {
		return GatewayTrafficMode{}, fmt.Errorf("decode gateway traffic mode: %w", err)
	}
	mode.Mode = normalizeTrafficMode(mode.Mode)
	mode.UpdatedAt = updatedAt
	return mode, nil
}

func (s *Store) CreateAcceptanceRun(ctx context.Context, input CreateAcceptanceRunInput) (AcceptanceRun, error) {
	input.ReleaseSHA = strings.TrimSpace(input.ReleaseSHA)
	input.APIImageDigest = strings.TrimSpace(input.APIImageDigest)
	input.WorkerImageDigest = strings.TrimSpace(input.WorkerImageDigest)
	input.APIKeyID = strings.TrimSpace(input.APIKeyID)
	input.UserID = strings.TrimSpace(input.UserID)
	input.Token = strings.TrimSpace(input.Token)
	input.EmulatorBaseURL = strings.TrimRight(strings.TrimSpace(input.EmulatorBaseURL), "/")
	input.CallbackURL = strings.TrimSpace(input.CallbackURL)
	input.CapacityProfile = strings.ToUpper(strings.TrimSpace(input.CapacityProfile))
	if input.CapacityProfile == "" {
		input.CapacityProfile = "P24"
	}
	if input.ReleaseSHA == "" || input.APIImageDigest == "" || input.WorkerImageDigest == "" ||
		input.APIKeyID == "" || input.UserID == "" || input.Token == "" || input.EmulatorBaseURL == "" ||
		input.CallbackURL == "" {
		return AcceptanceRun{}, errors.New("release SHA, image digests, API key, user, token, emulator URL, and callback URL are required")
	}
	if !acceptanceReleaseSHAPattern.MatchString(input.ReleaseSHA) ||
		!acceptanceDigestPattern.MatchString(input.APIImageDigest) ||
		!acceptanceDigestPattern.MatchString(input.WorkerImageDigest) {
		return AcceptanceRun{}, errors.New("release SHA and image digests must use full immutable identifiers")
	}
	if len(input.Token) < 32 {
		return AcceptanceRun{}, errors.New("acceptance token must contain at least 32 characters")
	}
	if !validAcceptanceURL(input.EmulatorBaseURL) || !validAcceptanceURL(input.CallbackURL) {
		return AcceptanceRun{}, errors.New("emulator and callback URLs must be absolute HTTP URLs without user information")
	}
	switch input.CapacityProfile {
	case "P24", "P28", "P32":
	default:
		return AcceptanceRun{}, errors.New("capacity profile must be P24, P28, or P32")
	}
	config, _ := json.Marshal(sanitizeAcceptanceMetadata(sanitizeJSONForStorage(input.Config)))
	tokenHash := acceptanceTokenSHA256(input.Token)
	if err := s.beginTransaction(ctx, func(tx pgx.Tx) error {
		_, err := cancelSafeAcceptanceTasksTx(ctx, tx, "")
		return err
	}); err != nil {
		return AcceptanceRun{}, err
	}
	return scanAcceptanceRun(s.pool.QueryRow(ctx, `
INSERT INTO gateway_acceptance_runs (
  release_sha, api_image_digest, worker_image_digest, api_key_id, user_id,
  token_sha256, emulator_base_url, callback_url, capacity_profile, config
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NULLIF($8, ''), $9, $10::jsonb)
RETURNING `+acceptanceRunColumns,
		input.ReleaseSHA, input.APIImageDigest, input.WorkerImageDigest, input.APIKeyID, input.UserID,
		tokenHash, input.EmulatorBaseURL, input.CallbackURL, input.CapacityProfile, config,
	))
}

func (s *Store) GetAcceptanceRun(ctx context.Context, runID string) (AcceptanceRun, error) {
	return scanAcceptanceRun(s.pool.QueryRow(ctx, `
SELECT `+acceptanceRunColumns+`
FROM gateway_acceptance_runs
WHERE id = $1::uuid`, strings.TrimSpace(runID)))
}

func (s *Store) ActivateAcceptanceRun(ctx context.Context, runID string) (GatewayTrafficMode, error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	defer rollbackTransaction(tx)

	var currentValue []byte
	if err := tx.QueryRow(ctx, `
SELECT value
FROM system_settings
WHERE setting_key = $1
FOR UPDATE`, SystemSettingGatewayTrafficMode).Scan(&currentValue); err != nil {
		return GatewayTrafficMode{}, err
	}
	var current GatewayTrafficMode
	if err := json.Unmarshal(currentValue, &current); err != nil {
		return GatewayTrafficMode{}, err
	}
	run, err := scanAcceptanceRun(tx.QueryRow(ctx, `
SELECT `+acceptanceRunColumns+`
FROM gateway_acceptance_runs
WHERE id = $1::uuid
FOR UPDATE`, strings.TrimSpace(runID)))
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	if run.Status != "pending" && run.Status != "failed" {
		return GatewayTrafficMode{}, ErrAcceptanceStateConflict
	}
	switch normalizeTrafficMode(current.Mode) {
	case "live":
	case "validation":
		if current.RunID == "" || current.RunID == run.ID {
			return GatewayTrafficMode{}, ErrAcceptanceStateConflict
		}
		previous, previousErr := scanAcceptanceRun(tx.QueryRow(ctx, `
SELECT `+acceptanceRunColumns+`
FROM gateway_acceptance_runs
WHERE id = $1::uuid
FOR UPDATE`, current.RunID))
		if previousErr != nil {
			return GatewayTrafficMode{}, previousErr
		}
		if previous.Status != "failed" ||
			previous.ReleaseSHA != current.ReleaseSHA ||
			previous.APIImageDigest != current.APIImageDigest ||
			previous.WorkerImageDigest != current.WorkerImageDigest {
			return GatewayTrafficMode{}, ErrAcceptanceStateConflict
		}
		var activeTasks int
		if err := tx.QueryRow(ctx, `
SELECT count(*)
FROM gateway_tasks
WHERE acceptance_run_id = $1::uuid
  AND status IN ('queued', 'running')`, previous.ID).Scan(&activeTasks); err != nil {
			return GatewayTrafficMode{}, err
		}
		if activeTasks != 0 {
			return GatewayTrafficMode{}, ErrAcceptanceStateConflict
		}
	default:
		return GatewayTrafficMode{}, ErrAcceptanceStateConflict
	}
	next := GatewayTrafficMode{
		Mode:              "validation",
		RunID:             run.ID,
		Revision:          current.Revision + 1,
		ReleaseSHA:        run.ReleaseSHA,
		APIImageDigest:    run.APIImageDigest,
		WorkerImageDigest: run.WorkerImageDigest,
	}
	value, _ := json.Marshal(next)
	if _, err := tx.Exec(ctx, `
UPDATE system_settings
SET value = $2::jsonb, updated_at = now()
WHERE setting_key = $1`, SystemSettingGatewayTrafficMode, value); err != nil {
		return GatewayTrafficMode{}, err
	}
	if _, err := tx.Exec(ctx, `
UPDATE gateway_acceptance_runs
SET status = 'running', started_at = COALESCE(started_at, now()),
    finished_at = NULL, failure_reason = NULL, updated_at = now()
WHERE id = $1::uuid`, run.ID); err != nil {
		return GatewayTrafficMode{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return GatewayTrafficMode{}, err
	}
	next.UpdatedAt = time.Now()
	return next, nil
}

func (s *Store) FinishAcceptanceRun(ctx context.Context, input FinishAcceptanceRunInput) (AcceptanceRun, error) {
	status := "failed"
	if input.Passed {
		status = "passed"
	}
	report, _ := json.Marshal(sanitizeAcceptanceMetadata(sanitizeJSONForStorage(input.Report)))
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return AcceptanceRun{}, err
	}
	defer rollbackTransaction(tx)
	run, err := scanAcceptanceRun(tx.QueryRow(ctx, `
UPDATE gateway_acceptance_runs
SET status = $2, report = $3::jsonb, failure_reason = NULLIF($4, ''),
    finished_at = now(), updated_at = now()
WHERE (
     id = $1::uuid
     AND status = 'running'
   )
   OR (
     id = $1::uuid
     AND $2::text = 'failed'
     AND status = 'pending'
   )
   OR (
     id = $1::uuid
     AND $2::text = 'failed'
     AND status = 'passed'
     AND EXISTS (
       SELECT 1
       FROM system_settings setting
       WHERE setting.setting_key = $5
         AND setting.value->>'mode' = 'validation'
         AND setting.value->>'runId' = $1::text
     )
   )
RETURNING `+acceptanceRunColumns,
		strings.TrimSpace(input.RunID), status, report, strings.TrimSpace(input.FailureReason),
		SystemSettingGatewayTrafficMode,
	))
	if err != nil {
		return AcceptanceRun{}, err
	}
	cancelled := int64(0)
	if !input.Passed {
		cancelled, err = cancelSafeAcceptanceTasksTx(ctx, tx, run.ID)
		if err != nil {
			return AcceptanceRun{}, err
		}
	}
	if err := tx.Commit(ctx); err != nil {
		return AcceptanceRun{}, err
	}
	if cancelled > 0 {
		s.notifyTaskAdmissionBestEffort(ctx, "*")
	}
	return run, nil
}

func (s *Store) RetryAcceptanceRun(ctx context.Context, runID string) (AcceptanceRun, error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return AcceptanceRun{}, err
	}
	defer rollbackTransaction(tx)
	var value []byte
	if err := tx.QueryRow(ctx, `
SELECT value
FROM system_settings
WHERE setting_key = $1
FOR UPDATE`, SystemSettingGatewayTrafficMode).Scan(&value); err != nil {
		return AcceptanceRun{}, err
	}
	var mode GatewayTrafficMode
	if err := json.Unmarshal(value, &mode); err != nil {
		return AcceptanceRun{}, err
	}
	if normalizeTrafficMode(mode.Mode) != "validation" || mode.RunID != strings.TrimSpace(runID) {
		return AcceptanceRun{}, ErrAcceptanceStateConflict
	}
	run, err := scanAcceptanceRun(tx.QueryRow(ctx, `
UPDATE gateway_acceptance_runs
SET status = 'running', report = '{}'::jsonb, failure_reason = NULL,
    finished_at = NULL, updated_at = now()
WHERE id = $1::uuid AND status = 'failed'
RETURNING `+acceptanceRunColumns, strings.TrimSpace(runID)))
	if err != nil {
		return AcceptanceRun{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return AcceptanceRun{}, err
	}
	return run, nil
}

func (s *Store) PromoteAcceptanceRun(ctx context.Context, input PromoteAcceptanceRunInput) (GatewayTrafficMode, error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	defer rollbackTransaction(tx)
	var value []byte
	if err := tx.QueryRow(ctx, `
SELECT value
FROM system_settings
WHERE setting_key = $1
FOR UPDATE`, SystemSettingGatewayTrafficMode).Scan(&value); err != nil {
		return GatewayTrafficMode{}, err
	}
	var current GatewayTrafficMode
	if err := json.Unmarshal(value, &current); err != nil {
		return GatewayTrafficMode{}, err
	}
	if normalizeTrafficMode(current.Mode) != "validation" ||
		current.RunID != strings.TrimSpace(input.RunID) ||
		current.Revision != input.Revision ||
		current.ReleaseSHA != strings.TrimSpace(input.ReleaseSHA) ||
		current.APIImageDigest != strings.TrimSpace(input.APIImageDigest) ||
		current.WorkerImageDigest != strings.TrimSpace(input.WorkerImageDigest) {
		return GatewayTrafficMode{}, ErrAcceptanceStateConflict
	}
	run, err := scanAcceptanceRun(tx.QueryRow(ctx, `
SELECT `+acceptanceRunColumns+`
FROM gateway_acceptance_runs
WHERE id = $1::uuid
FOR UPDATE`, current.RunID))
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	if run.Status != "passed" {
		return GatewayTrafficMode{}, ErrAcceptancePromotionGates
	}
	stagedConfigHash, _ := run.Config["validationConfigHash"].(string)
	if stagedConfigHash == "" {
		return GatewayTrafficMode{}, fmt.Errorf("%w: certified capacity profiles were not staged", ErrAcceptancePromotionGates)
	}
	if stagedConfigHash != strings.ToLower(strings.TrimSpace(input.ConfigHash)) {
		return GatewayTrafficMode{}, fmt.Errorf("%w: staged capacity config hash changed", ErrAcceptancePromotionGates)
	}
	stagedProfilesHash, _ := run.Config["validationProfilesHash"].(string)
	if stagedProfilesHash == "" || stagedProfilesHash != capacityProfileInputsHash(input.CapacityProfiles) {
		return GatewayTrafficMode{}, fmt.Errorf("%w: staged capacity profiles changed", ErrAcceptancePromotionGates)
	}
	if err := replaceCapacityProfilesTx(ctx, tx, run, input.ConfigHash, input.CapacityProfiles); err != nil {
		return GatewayTrafficMode{}, fmt.Errorf("%w: %v", ErrAcceptancePromotionGates, err)
	}
	next := GatewayTrafficMode{
		Mode: "live", RunID: run.ID, Revision: current.Revision + 1,
		ReleaseSHA: run.ReleaseSHA, APIImageDigest: run.APIImageDigest,
		WorkerImageDigest: run.WorkerImageDigest,
		ConfigHash:        strings.ToLower(strings.TrimSpace(input.ConfigHash)),
	}
	nextValue, _ := json.Marshal(next)
	if _, err := tx.Exec(ctx, `
UPDATE system_settings
SET value = $2::jsonb, updated_at = now()
WHERE setting_key = $1`, SystemSettingGatewayTrafficMode, nextValue); err != nil {
		return GatewayTrafficMode{}, err
	}
	if _, err := tx.Exec(ctx, `
UPDATE gateway_acceptance_runs
SET status = 'promoted', promoted_at = now(), updated_at = now()
WHERE id = $1::uuid`, current.RunID); err != nil {
		return GatewayTrafficMode{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return GatewayTrafficMode{}, err
	}
	next.UpdatedAt = time.Now()
	return next, nil
}

func (s *Store) PauseGatewayTraffic(
	ctx context.Context,
	input PauseGatewayTrafficInput,
) (GatewayTrafficMode, error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	defer rollbackTransaction(tx)
	var value []byte
	if err := tx.QueryRow(ctx, `
SELECT value
FROM system_settings
WHERE setting_key = $1
FOR UPDATE`, SystemSettingGatewayTrafficMode).Scan(&value); err != nil {
		return GatewayTrafficMode{}, err
	}
	var current GatewayTrafficMode
	if err := json.Unmarshal(value, &current); err != nil {
		return GatewayTrafficMode{}, err
	}
	reason := strings.TrimSpace(input.Reason)
	if normalizeTrafficMode(current.Mode) != "live" ||
		current.Revision != input.Revision ||
		current.ReleaseSHA != strings.TrimSpace(input.ReleaseSHA) ||
		current.APIImageDigest != strings.TrimSpace(input.APIImageDigest) ||
		current.WorkerImageDigest != strings.TrimSpace(input.WorkerImageDigest) ||
		reason == "" || len(reason) > 240 {
		return GatewayTrafficMode{}, ErrAcceptanceStateConflict
	}
	next := GatewayTrafficMode{
		Mode: "validation", RunID: current.RunID, Revision: current.Revision + 1,
		ReleaseSHA: current.ReleaseSHA, APIImageDigest: current.APIImageDigest,
		WorkerImageDigest: current.WorkerImageDigest, ConfigHash: current.ConfigHash,
	}
	nextValue, _ := json.Marshal(next)
	if _, err := tx.Exec(ctx, `
UPDATE system_settings
SET value = $2::jsonb, updated_at = now()
WHERE setting_key = $1`, SystemSettingGatewayTrafficMode, nextValue); err != nil {
		return GatewayTrafficMode{}, err
	}
	if current.RunID != "" {
		if _, err := tx.Exec(ctx, `
UPDATE gateway_acceptance_runs
SET report = jsonb_set(
      report,
      '{postPromotionPause}',
      jsonb_build_object('reason',$2::text,'pausedAt',now()),
      true
    ),
    updated_at = now()
WHERE id = $1::uuid`, current.RunID, reason); err != nil {
			return GatewayTrafficMode{}, err
		}
	}
	if err := tx.Commit(ctx); err != nil {
		return GatewayTrafficMode{}, err
	}
	next.UpdatedAt = time.Now()
	return next, nil
}

func (s *Store) AbortAcceptanceRun(ctx context.Context, input PromoteAcceptanceRunInput) (GatewayTrafficMode, error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	defer rollbackTransaction(tx)
	var value []byte
	if err := tx.QueryRow(ctx, `
SELECT value
FROM system_settings
WHERE setting_key = $1
FOR UPDATE`, SystemSettingGatewayTrafficMode).Scan(&value); err != nil {
		return GatewayTrafficMode{}, err
	}
	var current GatewayTrafficMode
	if err := json.Unmarshal(value, &current); err != nil {
		return GatewayTrafficMode{}, err
	}
	if normalizeTrafficMode(current.Mode) != "validation" ||
		current.RunID != strings.TrimSpace(input.RunID) ||
		current.Revision != input.Revision ||
		current.ReleaseSHA != strings.TrimSpace(input.ReleaseSHA) ||
		current.APIImageDigest != strings.TrimSpace(input.APIImageDigest) ||
		current.WorkerImageDigest != strings.TrimSpace(input.WorkerImageDigest) {
		return GatewayTrafficMode{}, ErrAcceptanceStateConflict
	}
	cancelled, err := cancelSafeAcceptanceTasksTx(ctx, tx, current.RunID)
	if err != nil {
		return GatewayTrafficMode{}, err
	}
	next := GatewayTrafficMode{Mode: "live", Revision: current.Revision + 1}
	nextValue, _ := json.Marshal(next)
	if _, err := tx.Exec(ctx, `
UPDATE system_settings
SET value = $2::jsonb, updated_at = now()
WHERE setting_key = $1`, SystemSettingGatewayTrafficMode, nextValue); err != nil {
		return GatewayTrafficMode{}, err
	}
	if _, err := tx.Exec(ctx, `
UPDATE gateway_acceptance_runs
SET status = 'aborted', finished_at = COALESCE(finished_at, now()), updated_at = now()
WHERE id = $1::uuid AND status IN ('running', 'failed', 'passed')`, current.RunID); err != nil {
		return GatewayTrafficMode{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return GatewayTrafficMode{}, err
	}
	if cancelled > 0 {
		s.notifyTaskAdmissionBestEffort(ctx, "*")
	}
	next.UpdatedAt = time.Now()
	return next, nil
}

func cancelSafeAcceptanceTasksTx(ctx context.Context, tx pgx.Tx, runID string) (int64, error) {
	runID = strings.TrimSpace(runID)
	tag, err := tx.Exec(ctx, `
UPDATE gateway_tasks task
SET status = 'cancelled',
    error = NULL,
    error_code = 'acceptance_run_aborted',
    error_message = 'acceptance run ended before upstream submission',
    billing_status = CASE
      WHEN gateway_user_id IS NULL THEN 'not_required'
      WHEN reservation_amount > 0 THEN 'pending'
      ELSE 'released'
    END,
    billing_updated_at = now(),
    remote_task_payload = '{}'::jsonb,
    locked_by = NULL,
    locked_at = NULL,
    heartbeat_at = NULL,
    execution_token = NULL,
    execution_lease_expires_at = NULL,
    finished_at = now(),
    updated_at = now()
WHERE task.run_mode = 'acceptance'
  AND task.status IN ('queued', 'running')
  AND COALESCE(task.remote_task_id, '') = ''
  AND NOT EXISTS (
    SELECT 1
    FROM gateway_task_attempts attempt
    WHERE attempt.task_id = task.id
      AND attempt.upstream_submission_status <> 'not_submitted'
  )
  AND (
    task.acceptance_run_id = NULLIF($1, '')::uuid
    OR (
      NULLIF($1, '') IS NULL
      AND EXISTS (
        SELECT 1
        FROM gateway_acceptance_runs run
        WHERE run.id = task.acceptance_run_id
          AND run.status IN ('failed', 'aborted')
      )
    )
  )`, runID)
	if err != nil {
		return 0, err
	}
	if _, err := tx.Exec(ctx, `
DELETE FROM gateway_task_param_preprocessing_logs log
USING gateway_task_attempts attempt, gateway_tasks task
WHERE log.attempt_id = attempt.id
  AND attempt.task_id = task.id
  AND attempt.upstream_submission_status = 'not_submitted'
  AND task.error_code = 'acceptance_run_aborted'
  AND (
    task.acceptance_run_id = NULLIF($1, '')::uuid
    OR (
      NULLIF($1, '') IS NULL
      AND EXISTS (
        SELECT 1
        FROM gateway_acceptance_runs run
        WHERE run.id = task.acceptance_run_id
          AND run.status IN ('failed', 'aborted')
      )
    )
  )`, runID); err != nil {
		return 0, err
	}
	if _, err := tx.Exec(ctx, `
DELETE FROM gateway_task_attempts attempt
USING gateway_tasks task
WHERE attempt.task_id = task.id
  AND attempt.upstream_submission_status = 'not_submitted'
  AND task.error_code = 'acceptance_run_aborted'
  AND (
    task.acceptance_run_id = NULLIF($1, '')::uuid
    OR (
      NULLIF($1, '') IS NULL
      AND EXISTS (
        SELECT 1
        FROM gateway_acceptance_runs run
        WHERE run.id = task.acceptance_run_id
          AND run.status IN ('failed', 'aborted')
      )
    )
  )`, runID); err != nil {
		return 0, err
	}
	if _, err := tx.Exec(ctx, `
INSERT INTO settlement_outbox (
  task_id, event_type, action, amount, currency, pricing_snapshot, payload,
  status, next_attempt_at
)
SELECT task.id, 'task.billing.release', 'release', task.reservation_amount,
       task.billing_currency, task.pricing_snapshot,
       jsonb_build_object('taskId', task.id, 'reason', 'acceptance_run_aborted'),
       'pending', now()
FROM gateway_tasks task
WHERE task.error_code = 'acceptance_run_aborted'
  AND task.billing_status = 'pending'
  AND task.reservation_amount > 0
  AND (
    task.acceptance_run_id = NULLIF($1, '')::uuid
    OR (
      NULLIF($1, '') IS NULL
      AND EXISTS (
        SELECT 1
        FROM gateway_acceptance_runs run
        WHERE run.id = task.acceptance_run_id
          AND run.status IN ('failed', 'aborted')
      )
    )
  )
ON CONFLICT (task_id, event_type) DO NOTHING`, runID); err != nil {
		return 0, err
	}
	if _, err := tx.Exec(ctx, `
UPDATE gateway_concurrency_leases lease
SET released_at = statement_timestamp()
FROM gateway_tasks task
WHERE lease.task_id = task.id
  AND lease.released_at IS NULL
  AND task.error_code = 'acceptance_run_aborted'
  AND (
    task.acceptance_run_id = NULLIF($1, '')::uuid
    OR (
      NULLIF($1, '') IS NULL
      AND EXISTS (
        SELECT 1
        FROM gateway_acceptance_runs run
        WHERE run.id = task.acceptance_run_id
          AND run.status IN ('failed', 'aborted')
      )
    )
  )`, runID); err != nil {
		return 0, err
	}
	if _, err := tx.Exec(ctx, `
DELETE FROM gateway_task_admissions admission
USING gateway_tasks task
WHERE admission.task_id = task.id
  AND task.error_code = 'acceptance_run_aborted'
  AND (
    task.acceptance_run_id = NULLIF($1, '')::uuid
    OR (
      NULLIF($1, '') IS NULL
      AND EXISTS (
        SELECT 1
        FROM gateway_acceptance_runs run
        WHERE run.id = task.acceptance_run_id
          AND run.status IN ('failed', 'aborted')
      )
    )
  )`, runID); err != nil {
		return 0, err
	}
	return tag.RowsAffected(), nil
}

func (s *Store) AuthorizeAcceptanceTask(ctx context.Context, runID string, token string, user *auth.User) (string, error) {
	mode, err := s.GetGatewayTrafficMode(ctx)
	if err != nil {
		return "", err
	}
	runID = strings.TrimSpace(runID)
	token = strings.TrimSpace(token)
	if mode.Mode == "live" {
		if runID != "" || token != "" {
			return "", ErrAcceptanceRunNotActive
		}
		return "", nil
	}
	if runID == "" || token == "" || runID != mode.RunID {
		return "", ErrProductionTrafficPaused
	}
	if user == nil || strings.TrimSpace(user.APIKeyID) == "" {
		return "", ErrAcceptanceNotAuthorized
	}
	var activeRunID, apiKeyID, userID, tokenHash, status string
	var participantAuthorized bool
	err = s.pool.QueryRow(ctx, `
SELECT id::text, api_key_id, user_id, token_sha256, status,
       (
         (api_key_id = $2 AND user_id = $3)
         OR EXISTS (
           SELECT 1
           FROM jsonb_array_elements(
             CASE
               WHEN jsonb_typeof(config->'participants') = 'array'
                 THEN config->'participants'
               ELSE '[]'::jsonb
             END
           ) participant
           WHERE participant->>'apiKeyId' = $2
             AND participant->>'userId' = $3
         )
       )
FROM gateway_acceptance_runs
WHERE id = $1::uuid`, runID, strings.TrimSpace(user.APIKeyID), strings.TrimSpace(user.ID)).Scan(
		&activeRunID, &apiKeyID, &userID, &tokenHash, &status, &participantAuthorized,
	)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return "", ErrAcceptanceNotAuthorized
		}
		return "", err
	}
	if status != "running" || !participantAuthorized ||
		subtle.ConstantTimeCompare([]byte(tokenHash), []byte(acceptanceTokenSHA256(token))) != 1 {
		return "", ErrAcceptanceNotAuthorized
	}
	return activeRunID, nil
}

func (s *Store) AcceptanceCandidateOverride(ctx context.Context, runID string) (string, string, error) {
	var baseURL, status string
	err := s.pool.QueryRow(ctx, `
SELECT emulator_base_url, status
FROM gateway_acceptance_runs
WHERE id = $1::uuid`, strings.TrimSpace(runID)).Scan(&baseURL, &status)
	if err != nil {
		return "", "", err
	}
	if status != "running" && status != "passed" {
		return "", "", ErrAcceptanceRunNotActive
	}
	return strings.TrimRight(strings.TrimSpace(baseURL), "/"), "acceptance-protocol-emulator", nil
}

func (s *Store) AcceptanceCallbackURL(ctx context.Context, taskID string) (string, bool, error) {
	var callbackURL string
	err := s.pool.QueryRow(ctx, `
SELECT COALESCE(run.callback_url, '')
FROM gateway_tasks task
JOIN gateway_acceptance_runs run ON run.id = task.acceptance_run_id
WHERE task.id = $1::uuid`, strings.TrimSpace(taskID)).Scan(&callbackURL)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return "", false, nil
		}
		return "", false, err
	}
	return strings.TrimSpace(callbackURL), true, nil
}

func normalizeTrafficMode(value string) string {
	if strings.EqualFold(strings.TrimSpace(value), "validation") {
		return "validation"
	}
	return "live"
}

func acceptanceTokenSHA256(token string) string {
	sum := sha256.Sum256([]byte(strings.TrimSpace(token)))
	return hex.EncodeToString(sum[:])
}

func validAcceptanceURL(raw string) bool {
	parsed, err := url.Parse(strings.TrimSpace(raw))
	if err != nil || parsed.User != nil || parsed.Host == "" {
		return false
	}
	return parsed.Scheme == "http" || parsed.Scheme == "https"
}

func sanitizeAcceptanceMetadata(value any) any {
	switch typed := value.(type) {
	case map[string]any:
		next := make(map[string]any, len(typed))
		for key, item := range typed {
			normalized := strings.ToLower(strings.NewReplacer("_", "", "-", "", ".", "").Replace(key))
			if strings.Contains(normalized, "password") ||
				strings.Contains(normalized, "secret") ||
				strings.Contains(normalized, "credential") ||
				strings.Contains(normalized, "connectionstring") ||
				strings.Contains(normalized, "databaseurl") ||
				(strings.Contains(normalized, "token") && !strings.HasSuffix(normalized, "count")) {
				next[key] = "[REDACTED]"
				continue
			}
			next[key] = sanitizeAcceptanceMetadata(item)
		}
		return next
	case []any:
		next := make([]any, len(typed))
		for index, item := range typed {
			next[index] = sanitizeAcceptanceMetadata(item)
		}
		return next
	default:
		return value
	}
}

const acceptanceRunColumns = `
id::text, status, release_sha, api_image_digest, worker_image_digest,
api_key_id, user_id, emulator_base_url, COALESCE(callback_url, ''),
capacity_profile, config, report, COALESCE(failure_reason, ''),
COALESCE(started_at::text, ''), COALESCE(finished_at::text, ''),
COALESCE(promoted_at::text, ''), created_at, updated_at`

func scanAcceptanceRun(scanner taskScanner) (AcceptanceRun, error) {
	var run AcceptanceRun
	var config, report []byte
	err := scanner.Scan(
		&run.ID, &run.Status, &run.ReleaseSHA, &run.APIImageDigest, &run.WorkerImageDigest,
		&run.APIKeyID, &run.UserID, &run.EmulatorBaseURL, &run.CallbackURL,
		&run.CapacityProfile, &config, &report, &run.FailureReason,
		&run.StartedAt, &run.FinishedAt, &run.PromotedAt, &run.CreatedAt, &run.UpdatedAt,
	)
	if err != nil {
		return AcceptanceRun{}, err
	}
	run.Config = decodeObject(config)
	run.Report = decodeObject(report)
	return run, nil
}
