import { sql } from 'drizzle-orm';
import type { DrizzleClient, TxDrizzleClient } from '@/server/db/client.js';

export interface GroupDealExecutionProgressEntry {
  reservationId: string;
  idempotencyKey: string;
  prePurchaseId: string;
  providerTransactionId: string;
  captureAmount: string;
  totalAgorot: number;
  status: 'pending_capture' | 'captured' | 'completed' | 'failed';
  error?: string;
}

export interface GroupDealExecutionProgress {
  version: 1;
  reservations: Record<string, GroupDealExecutionProgressEntry>;
}

export interface GroupDealExecutionBegin {
  fresh: boolean;
  commandId: string;
  status: 'CLAIMED' | 'COMPLETED';
  progress: GroupDealExecutionProgress;
  result: Record<string, unknown> | null;
}

export interface GroupDealExecutionStateStore {
  begin(groupDealId: string): Promise<GroupDealExecutionBegin>;
  saveProgress(commandId: string, progress: GroupDealExecutionProgress): Promise<void>;
  complete(commandId: string, result: Record<string, unknown>): Promise<void>;
}

interface CommandRow {
  id: string;
  status: 'CLAIMED' | 'COMPLETED' | 'FAILED';
  result_payload: unknown;
}

interface CommandRowsResult {
  rows: unknown[];
}

interface StoredExecutionPayload {
  progress?: GroupDealExecutionProgress;
  result?: Record<string, unknown>;
}

function commandRows(value: unknown): CommandRow[] {
  return ((value as CommandRowsResult).rows ?? []) as CommandRow[];
}

function emptyProgress(): GroupDealExecutionProgress {
  return { version: 1, reservations: {} };
}

function asObject(value: unknown): Record<string, unknown> | null {
  if (value == null) return null;
  if (typeof value === 'string') return JSON.parse(value) as Record<string, unknown>;
  return value as Record<string, unknown>;
}

function asProgress(value: unknown): GroupDealExecutionProgress {
  const payload = asObject(value);
  if (!payload) return emptyProgress();
  const storedProgress = (payload as StoredExecutionPayload).progress;
  if (storedProgress?.reservations && typeof storedProgress.reservations === 'object') {
    return {
      version: 1,
      reservations: storedProgress.reservations,
    };
  }
  const reservations = payload.reservations;
  if (!reservations || typeof reservations !== 'object') return emptyProgress();
  return {
    version: 1,
    reservations: reservations as GroupDealExecutionProgress['reservations'],
  };
}

function initialPayload(): StoredExecutionPayload {
  return { progress: emptyProgress(), result: undefined };
}

function asResult(value: unknown): Record<string, unknown> | null {
  const payload = asObject(value);
  if (!payload) return null;
  const result = (payload as StoredExecutionPayload).result;
  return result && typeof result === 'object' ? result : payload;
}

export function createGroupDealExecutionStateStore(
  db: DrizzleClient | TxDrizzleClient,
): GroupDealExecutionStateStore {
  return {
    async begin(groupDealId) {
      const commandKey = `group-deal.execute:${groupDealId}`;
      const inserted = (await db.execute(sql`
        INSERT INTO "command_records" (
          "command_key",
          "command_type",
          "aggregate_type",
          "aggregate_id",
          "payload_hash",
          "payload",
          "status",
          "claimed_by",
          "claimed_at",
          "result_payload"
        )
        VALUES (
          ${commandKey},
          'group_deal.execute',
          'group_deal',
          ${groupDealId}::uuid,
          'group-deal.execute:v1',
          ${JSON.stringify({ groupDealId })}::jsonb,
          'CLAIMED',
          'group-deal.execute',
          now(),
          ${JSON.stringify(initialPayload())}::jsonb
        )
        ON CONFLICT ("command_key") DO NOTHING
        RETURNING "id", "status", "result_payload"
      `)) as unknown;

      const insertedRow = commandRows(inserted)[0];
      if (insertedRow) {
        return {
          fresh: true,
          commandId: insertedRow.id,
          status: insertedRow.status === 'COMPLETED' ? 'COMPLETED' : 'CLAIMED',
          progress: asProgress(insertedRow.result_payload),
          result: null,
        };
      }

      const existing = (await db.execute(sql`
        SELECT "id", "status", "result_payload"
        FROM "command_records"
        WHERE "command_key" = ${commandKey}
        LIMIT 1
      `)) as unknown;
      const row = commandRows(existing)[0];
      if (!row || row.status === 'FAILED') {
        throw new Error(`group-deal execution state missing or failed for ${groupDealId}`);
      }
      return {
        fresh: false,
        commandId: row.id,
        status: row.status,
        progress: asProgress(row.result_payload),
        result: asResult(row.result_payload),
      };
    },

    async saveProgress(commandId, progress) {
      await db.execute(sql`
        UPDATE "command_records"
        SET "result_payload" = jsonb_set(
          COALESCE("result_payload", '{}'::jsonb),
          '{progress}',
          ${JSON.stringify(progress)}::jsonb,
          true
        )
        WHERE "id" = ${commandId}::uuid
          AND "status" = 'CLAIMED'
      `);
    },

    async complete(commandId, result) {
      await db.execute(sql`
        UPDATE "command_records"
        SET
          "status" = 'COMPLETED',
          "completed_at" = now(),
          "result_payload" = jsonb_set(
            COALESCE("result_payload", '{}'::jsonb),
            '{result}',
            ${JSON.stringify(result)}::jsonb,
            true
          )
        WHERE "id" = ${commandId}::uuid
          AND "status" = 'CLAIMED'
      `);
    },
  };
}
