import { PGlite } from '@electric-sql/pglite'
import { drizzle } from 'drizzle-orm/pglite'
import type { TransactionalDatabase } from '@platform-modules/db'
import { appendEntry, ledgerEntries, ledgerSchema } from '@platform-modules/ledger'
import type { LedgerSeam } from '../index.js'
import { validateSubscriptionStatus } from './types.js'
import type {
  CreateSubscriptionInput,
  ParsedSubscriptionWebhook,
  Subscription,
} from './types.js'
import type { SubscriptionProvider } from './port.js'

type TestSchema = typeof ledgerSchema

export async function createSubscriptionsTestDb(): Promise<TransactionalDatabase<TestSchema>> {
  const client = new PGlite()
  const db = drizzle(client, { schema: ledgerSchema }) as unknown as TransactionalDatabase<TestSchema>
  await client.exec(`
    CREATE TABLE ledger_entries (
      id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
      delta bigint NOT NULL,
      currency text,
      reason text NOT NULL,
      ref jsonb,
      idempotency_key text NOT NULL,
      created_at timestamptz NOT NULL DEFAULT NOW()
    );
    CREATE UNIQUE INDEX ledger_entries_idempotency_key_uq ON ledger_entries (idempotency_key);
  `)
  return db
}

export function asLedgerSeam(): LedgerSeam {
  return { appendEntry: appendEntry as LedgerSeam['appendEntry'] }
}

export async function listLedgerEntries(db: TransactionalDatabase<TestSchema>) {
  return db.select().from(ledgerEntries)
}

export type TestSubscriptionWebhook =
  | {
      type: 'subscription.settlement'
      subscriptionId: string
      period: string
      amountMinor: bigint
      currency: string
      eventId?: string
    }
  | {
      type: 'subscription.other'
      raw?: unknown
      eventId?: string
    }

export interface TestSubscriptionProvider extends SubscriptionProvider {
  readonly uniqueCreateCount: number
  parseWebhook(rawEvent: unknown): Promise<ParsedSubscriptionWebhook>
}

export function createTestSubscriptionProvider(): TestSubscriptionProvider {
  const subscriptions = new Map<string, Subscription>()
  const createByIdempotencyKey = new Map<string, string>()
  let uniqueCreateCount = 0

  return {
    get uniqueCreateCount() {
      return uniqueCreateCount
    },

    async createSubscription(input: CreateSubscriptionInput): Promise<Subscription> {
      const existingId = createByIdempotencyKey.get(input.idempotencyKey)
      if (existingId) {
        const existing = subscriptions.get(existingId)
        if (!existing) throw new Error(`fixture missing subscription ${existingId}`)
        return existing
      }

      uniqueCreateCount += 1
      const subscription: Subscription = {
        id: `sub_${uniqueCreateCount}`,
        status: validateSubscriptionStatus(input.status ?? 'active'),
        currentPeriod: input.currentPeriod,
        planId: input.planId,
      }
      subscriptions.set(subscription.id, subscription)
      createByIdempotencyKey.set(input.idempotencyKey, subscription.id)
      return subscription
    },

    async cancelSubscription(id: string): Promise<void> {
      const subscription = subscriptions.get(id)
      if (!subscription) throw new Error(`unknown subscription ${id}`)
      subscriptions.set(id, { ...subscription, status: 'canceled' })
    },

    async getSubscription(id: string): Promise<Subscription> {
      const subscription = subscriptions.get(id)
      if (!subscription) throw new Error(`unknown subscription ${id}`)
      return subscription
    },

    async parseWebhook(rawEvent: unknown): Promise<ParsedSubscriptionWebhook> {
      if (!rawEvent || typeof rawEvent !== 'object') {
        return { kind: 'other', raw: rawEvent, eventId: 'subscription:other' }
      }

      const event = rawEvent as Partial<TestSubscriptionWebhook>
      if (event.type === 'subscription.settlement') {
        if (
          typeof event.subscriptionId !== 'string' ||
          typeof event.period !== 'string' ||
          typeof event.currency !== 'string' ||
          typeof event.amountMinor !== 'bigint'
        ) {
          throw new Error('invalid settlement webhook payload')
        }
        return {
          kind: 'settlement',
          subscriptionId: event.subscriptionId,
          period: event.period,
          amountMinor: event.amountMinor,
          currency: event.currency,
          eventId: typeof event.eventId === 'string' ? event.eventId : undefined,
        }
      }

      return {
        kind: 'other',
        raw: 'raw' in event ? event.raw ?? rawEvent : rawEvent,
        eventId: typeof event.eventId === 'string' ? event.eventId : 'subscription:other',
      }
    },
  }
}
