import { eq, sql } from 'drizzle-orm'
import type { MiddlewareHandler } from 'hono'
import { describe, expect, it } from 'vitest'

import { createPgliteClient } from '../../../../packages/db/src/postgres/pglite.ts'
import { createApp } from '../../src/index.js'
import { periodKey, seedPeriodWallet } from '../../src/lib/wallet.js'
import { createPluginRoute } from '../../src/routes/plugin.js'
import { pressZoneInitSql, pressZoneSchema, walletBalances } from '../../src/schema.js'

type TestDb = ReturnType<typeof createPgliteClient<typeof pressZoneSchema>>

async function createTestDb(): Promise<TestDb> {
  const db = createPgliteClient({ schema: pressZoneSchema })

  for (const statement of pressZoneInitSql.split(';').map((part) => part.trim()).filter(Boolean)) {
    await db.execute(sql.raw(statement))
  }

  return db
}

async function readJson(response: Response): Promise<unknown> {
  return response.json()
}

async function readBalance(db: TestDb, ownerId: string): Promise<bigint> {
  const [row] = await db
    .select({ balance: walletBalances.balance })
    .from(walletBalances)
    .where(eq(walletBalances.ownerId, ownerId))
    .limit(1)

  return row?.balance ?? 0n
}

function createAuthMiddleware(accountId: string): MiddlewareHandler {
  return async (context, next) => {
    context.set('principal', {
      account: accountId,
      userId: 'site-user',
      scopes: ['plugin:translate'],
    })
    context.set('capabilities', ['plugin:translate'])
    await next()
  }
}

const allowAccess: MiddlewareHandler = async (_context, next) => {
  await next()
}

describe('HTTP integration: metered debit flow', () => {
  it('debits once on success and returns 402 without an extra debit when credits run out', async () => {
    const db = await createTestDb()
    const walletKey = periodKey('acct_123', 'translate', '2026-07')
    await seedPeriodWallet(db, walletKey, 5n)
    const app = createApp({
      pluginRoute: createPluginRoute({
        auth: createAuthMiddleware('acct_123'),
        access: allowAccess,
        cost: 3n,
        currentPeriod: () => '2026-07',
        execute: async () => ({ translatedText: 'shalom' }),
        getDb: () => db,
        plugin: 'translate',
      }),
    })

    const first = await app.request('http://press-zone.test/api/api/plugin/translate', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ text: 'hello' }),
    })

    expect(first.status).toBe(200)
    await expect(readJson(first)).resolves.toEqual({
      data: {
        translatedText: 'shalom',
      },
    })
    expect(await readBalance(db, walletKey)).toBe(2n)

    const second = await app.request('http://press-zone.test/api/api/plugin/translate', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ text: 'goodbye' }),
    })

    expect(second.status).toBe(402)
    await expect(readJson(second)).resolves.toEqual({
      error: {
        code: 'INSUFFICIENT_CREDITS',
        message: 'Insufficient credits',
      },
    })
    expect(await readBalance(db, walletKey)).toBe(2n)
  })
})
