import { suite } from '../lib/context.js'

suite('Accounting', { tags: ['accounting'] }, (t) => {
  // ─── Invoices: CRUD ───────────────────────────────────────────────────────

  t.test('creates a manual invoice', { tags: ['invoices', 'create'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Invoice Test Acct', billingMode: 'consolidated' })
    const res = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [
        { description: 'Service A', qty: 2, unitPrice: 50, taxRate: 0.0825 },
        { description: 'Service B', qty: 1, unitPrice: 100 },
      ],
    })
    t.assert.status(res, 201)
    t.assert.ok(res.data.invoiceNumber)
    t.assert.equal(res.data.status, 'draft')
    t.assert.ok(parseFloat(res.data.total) > 0)
    t.assert.equal(res.data.balance, res.data.total)
  })

  t.test('rejects invoice without line items', { tags: ['invoices', 'validation'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Empty Invoice Acct', billingMode: 'consolidated' })
    const res = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [],
    })
    t.assert.status(res, 400)
  })

  t.test('lists invoices with pagination', { tags: ['invoices', 'list'] }, async () => {
    const res = await t.api.get('/v1/invoices', { page: 1, limit: 25 })
    t.assert.status(res, 200)
    t.assert.ok(res.data.data)
    t.assert.ok(res.data.pagination)
  })

  t.test('gets invoice detail with line items', { tags: ['invoices', 'detail'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Detail Invoice Acct', billingMode: 'consolidated' })
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Widget', qty: 3, unitPrice: 25 }],
    })

    const res = await t.api.get(`/v1/invoices/${inv.data.id}`)
    t.assert.status(res, 200)
    t.assert.ok(res.data.lineItems)
    t.assert.equal(res.data.lineItems.length, 1)
    t.assert.ok(res.data.payments)
  })

  t.test('returns 404 for missing invoice', { tags: ['invoices', 'detail'] }, async () => {
    const res = await t.api.get('/v1/invoices/00000000-0000-0000-0000-000000000000')
    t.assert.status(res, 404)
  })

  // ─── Invoice Workflow ─────────────────────────────────────────────────────

  t.test('sends a draft invoice', { tags: ['invoices', 'workflow'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Send Invoice Acct', billingMode: 'consolidated' })
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Send Test', qty: 1, unitPrice: 100 }],
    })

    const res = await t.api.post(`/v1/invoices/${inv.data.id}/send`)
    t.assert.status(res, 200)
    t.assert.equal(res.data.status, 'sent')
  })

  t.test('applies payment to invoice', { tags: ['invoices', 'payment'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Payment Invoice Acct', billingMode: 'consolidated' })
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Pay Test', qty: 1, unitPrice: 200 }],
    })
    await t.api.post(`/v1/invoices/${inv.data.id}/send`)

    // Partial payment
    const res1 = await t.api.post(`/v1/invoices/${inv.data.id}/apply-payment`, { amount: 100 })
    t.assert.status(res1, 200)
    t.assert.ok(res1.data.id)

    // Check invoice updated
    const detail = await t.api.get(`/v1/invoices/${inv.data.id}`)
    t.assert.equal(detail.data.status, 'partial')
    t.assert.equal(detail.data.amountPaid, '100.00')
    t.assert.equal(detail.data.balance, '100.00')

    // Full payment
    const res2 = await t.api.post(`/v1/invoices/${inv.data.id}/apply-payment`, { amount: 100 })
    t.assert.status(res2, 200)

    const final = await t.api.get(`/v1/invoices/${inv.data.id}`)
    t.assert.equal(final.data.status, 'paid')
    t.assert.equal(final.data.balance, '0.00')
  })

  t.test('rejects overpayment', { tags: ['invoices', 'payment', 'validation'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Overpay Acct', billingMode: 'consolidated' })
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Small Item', qty: 1, unitPrice: 50 }],
    })
    await t.api.post(`/v1/invoices/${inv.data.id}/send`)

    const res = await t.api.post(`/v1/invoices/${inv.data.id}/apply-payment`, { amount: 100 })
    t.assert.status(res, 400)
  })

  t.test('voids an invoice', { tags: ['invoices', 'void'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Void Invoice Acct', billingMode: 'consolidated' })
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Void Test', qty: 1, unitPrice: 75 }],
    })
    await t.api.post(`/v1/invoices/${inv.data.id}/send`)

    const res = await t.api.post(`/v1/invoices/${inv.data.id}/void`, { reason: 'Customer changed mind' })
    t.assert.status(res, 200)
    t.assert.equal(res.data.status, 'void')
    t.assert.equal(res.data.balance, '0')
  })

  t.test('writes off a bad debt', { tags: ['invoices', 'write-off'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'WriteOff Acct', billingMode: 'consolidated' })
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Bad Debt Item', qty: 1, unitPrice: 300 }],
    })
    await t.api.post(`/v1/invoices/${inv.data.id}/send`)

    const res = await t.api.post(`/v1/invoices/${inv.data.id}/write-off`, { reason: 'Uncollectable' })
    t.assert.status(res, 200)
    t.assert.equal(res.data.status, 'written_off')
  })

  // ─── Account Balance ──────────────────────────────────────────────────────

  t.test('tracks account balance on invoice send', { tags: ['balance'] }, async () => {
    const acct = await t.api.post('/v1/accounts', { name: 'Balance Track Acct', billingMode: 'consolidated' })

    // Create and send invoice (which triggers AR balance update via service)
    const inv = await t.api.post('/v1/invoices', {
      accountId: acct.data.id,
      issueDate: '2026-04-06',
      dueDate: '2026-05-06',
      lineItems: [{ description: 'Balance Test', qty: 1, unitPrice: 150 }],
    })

    const balRes = await t.api.get(`/v1/accounts/${acct.data.id}/balance`)
    t.assert.status(balRes, 200)
    t.assert.ok(balRes.data.accountId)
  })

  // ─── CSV Export ───────────────────────────────────────────────────────────

  t.test('exports invoices as CSV', { tags: ['export'] }, async () => {
    const res = await t.api.get('/v1/invoices/export')
    t.assert.status(res, 200)
    // CSV response should have headers
    const text = typeof res.data === 'string' ? res.data : JSON.stringify(res.data)
    t.assert.contains(text, 'Invoice Number')
  })

  // ─── AR Aging ─────────────────────────────────────────────────────────────

  t.test('returns AR aging report', { tags: ['reports'] }, async () => {
    const res = await t.api.get('/v1/invoices/aging')
    t.assert.status(res, 200)
    t.assert.ok(res.data.current !== undefined)
    t.assert.ok(res.data.days30 !== undefined)
    t.assert.ok(res.data.days60 !== undefined)
    t.assert.ok(res.data.days90plus !== undefined)
    t.assert.ok(res.data.total !== undefined)
  })

  // ─── Outstanding Accounts ─────────────────────────────────────────────────

  t.test('lists outstanding accounts', { tags: ['balance'] }, async () => {
    const res = await t.api.get('/v1/accounts/outstanding', { page: 1, limit: 25 })
    t.assert.status(res, 200)
    t.assert.ok(res.data.data)
    t.assert.ok(res.data.pagination)
  })
})
