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

import { createPgliteClient } from '../../../../packages/db/src/postgres/pglite.ts'
import { MockInvoiceProvider } from '../../../../packages/invoicing/src/mock-provider.ts'
import { createApp } from '../../src/index.js'
import { issueSettlementInvoice } from '../../src/lib/billing-doc.js'
import { createStaffRoute } from '../../src/routes/staff.js'
import {
  invoiceDocument,
  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 createProtectedMiddleware(): MiddlewareHandler {
  return async (context, next) => {
    context.set('principal', {
      account: 'staff-account',
      userId: 'staff-user',
      scopes: ['staff'],
    })
    context.set('capabilities', ['staff'])
    await next()
  }
}

describe('HTTP integration: staff refund flow', () => {
  it('refunds through the mounted app path, issues one credit note, and credits the wallet balance', async () => {
    const db = await createTestDb()
    const morningProvider = new MockInvoiceProvider()
    const walletKey = 'acct-1:translate:2026-07'

    await db.insert(walletBalances).values({
      ownerId: walletKey,
      balance: 20n,
      updatedAt: new Date('2026-07-04T00:00:00.000Z'),
    })

    const app = createApp({
      staffRoute: createStaffRoute({
        adjustCredits: async () => ({ balance: 0n }),
        audit: async () => undefined,
        reserveRefund: async () => ({ status: 'reserved' }),
        releaseRefundReservation: async () => undefined,
        recordRefundCredit: async ({
          chargeKey,
          refundId,
          refundKey,
          walletKey: ownerId,
          amountMinor,
          currency,
          customer,
        }) => {
          const result = await issueSettlementInvoice(
            Object.assign(db, {
              morningCredential: {
                apiUser: 'api-user@example.test',
                apiPass: 'secret',
                companyId: 'company-123',
              },
              morningProvider,
              invoiceCurrency: currency,
              invoiceDocType: 'credit_note' as const,
            }),
            {
              supplier: { country: 'IL' },
              customer: {
                country: 'IL',
                ...customer,
              },
              supplyType: 'digital',
              lineItems: [
                {
                  description: `Refund ${chargeKey} (${refundId})`,
                  quantity: 1,
                  unitAmountMinor: amountMinor,
                },
              ],
              idempotencyKey: refundKey,
            },
          )

          if (!result.ok) {
            throw new Error(`${result.error.code}: ${result.error.message}`)
          }

          const [row] = await db
            .update(walletBalances)
            .set({
              balance: sql`${walletBalances.balance} + ${amountMinor}`,
              updatedAt: new Date('2026-07-04T00:00:00.000Z'),
            })
            .where(eq(walletBalances.ownerId, ownerId))
            .returning({ balance: walletBalances.balance })

          if (!row) {
            throw new Error(`wallet not found: ${ownerId}`)
          }

          return { balance: row.balance, creditNote: result.result }
        },
        listAudit: async () => ({ items: [], nextCursor: null }),
        refundProvider: {
          refund: vi.fn(async () => ({
            kind: 'refunded' as const,
            refundKey: 'paypal-refund-1',
            chargeKey: 'charge-1',
            providerRef: 'paypal-refund-1',
            amount: 30,
            currency: 'ILS',
          })),
        },
        setTier: async () => undefined,
      }),
      protectedMiddleware: createProtectedMiddleware(),
    })

    const response = await app.request('http://press-zone.test/api/staff/refund', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        walletKey,
        chargeKey: 'charge-1',
        refundId: 'refund-request-1',
        amountMinor: '30',
        currency: 'ILS',
        customer: {
          name: 'Acme Ltd',
          email: 'billing@example.test',
          taxId: '514000000',
        },
      }),
    })

    expect(response.status).toBe(200)
    await expect(readJson(response)).resolves.toEqual({
      data: {
        status: 'refunded',
        balance: '50',
        creditNote: {
          documentId: expect.any(String),
          documentNumber: expect.any(String),
          documentUrl: expect.any(String),
        },
      },
    })
    expect(await readBalance(db, walletKey)).toBe(50n)

    const [storedDocument] = await db
      .select({
        idempotencyKey: invoiceDocument.idempotencyKey,
        documentId: invoiceDocument.documentId,
      })
      .from(invoiceDocument)
      .where(eq(invoiceDocument.idempotencyKey, 'staff-refund:charge-1:refund-request-1'))
      .limit(1)

    expect(storedDocument).toMatchObject({
      idempotencyKey: 'staff-refund:charge-1:refund-request-1',
      documentId: expect.any(String),
    })
    expect(morningProvider.issueCallCount.current).toBe(1)
    expect(morningProvider.calls[0]?.spec.docType).toBe('credit_note')
  })
})
