import { describe, expect, it, vi } from 'vitest'
import {
  createInvoiceFromProposal,
  getProposalInvoiceLink,
} from '../src/queries/proposal-invoice'

describe('getProposalInvoiceLink', () => {
  it('returns null when no invoice is linked to the proposal', async () => {
    const db = {
      select: vi.fn(() => ({
        from: vi.fn(() => ({
          where: vi.fn(() => ({
            limit: vi.fn().mockResolvedValue([]),
          })),
        })),
      })),
    }

    await expect(getProposalInvoiceLink(db as never, 'tenant-1', 'proposal-1')).resolves.toBeNull()
  })
})

describe('createInvoiceFromProposal', () => {
  it('rejects proposals that are not accepted', async () => {
    const tx = {
      select: vi
        .fn()
        .mockImplementationOnce(() => ({
          from: vi.fn(() => ({
            where: vi.fn(() => ({
              limit: vi.fn().mockResolvedValue([
                { id: 'proposal-1', tenantId: 'tenant-1', status: 'sent' },
              ]),
            })),
          })),
        })),
      insert: vi.fn(),
    }

    const db = {
      transaction: vi.fn(async (fn: (trx: typeof tx) => Promise<unknown>) => fn(tx)),
    }

    await expect(
      createInvoiceFromProposal(db as never, 'tenant-1', 'proposal-1', 'user-1', {
        issue_date: '2026-07-03',
        due_date: '2026-08-02',
        lines: [{ description: 'Line', quantity: 1, unit_price: 100 }],
      }),
    ).rejects.toThrow('Proposal must be ACCEPTED before creating an invoice')
  })

  it('creates a draft invoice from client-edited lines with tenant tax settings', async () => {
    const insertInvoiceValues = vi.fn(() => ({
      returning: vi.fn().mockResolvedValue([{ id: 'invoice-1', invoiceNumber: null }]),
    }))
    const insertInvoiceLinesValues = vi.fn().mockResolvedValue(undefined)
    const insertAuditValues = vi.fn().mockResolvedValue(undefined)
    const insertMock = vi
      .fn()
      .mockImplementationOnce(() => ({ values: insertInvoiceValues }))
      .mockImplementationOnce(() => ({ values: insertInvoiceLinesValues }))
      .mockImplementationOnce(() => ({ values: insertAuditValues }))

    const tx = {
      select: vi
        .fn()
        .mockImplementationOnce(() => ({
          from: vi.fn(() => ({
            where: vi.fn(() => ({
              limit: vi.fn().mockResolvedValue([
                {
                  id: 'proposal-1',
                  tenantId: 'tenant-1',
                  customerId: 'customer-1',
                  status: 'accepted',
                  content: null,
                },
              ]),
            })),
          })),
        }))
        .mockImplementationOnce(() => ({
          from: vi.fn(() => ({
            where: vi.fn(() => ({
              limit: vi.fn().mockResolvedValue([]),
            })),
          })),
        }))
        .mockImplementationOnce(() => ({
          from: vi.fn(() => ({
            where: vi.fn(() => ({
              limit: vi.fn().mockResolvedValue([{ defaultTaxRate: '0.17' }]),
            })),
          })),
        })),
      insert: insertMock,
    }

    const db = {
      transaction: vi.fn(async (fn: (trx: typeof tx) => Promise<unknown>) => fn(tx)),
    }

    const result = await createInvoiceFromProposal(
      db as never,
      'tenant-1',
      'proposal-1',
      'user-1',
      {
        issue_date: '2026-07-03',
        due_date: '2026-08-02',
        note: 'PO 42',
        lines: [
          {
            description: 'Website Redesign',
            quantity: 2,
            unit_price: 5000,
            discount_pct: 10,
          },
          {
            description: 'Training',
            quantity: 1,
            unit_price: 2500,
          },
        ],
      },
    )

    expect(insertInvoiceValues).toHaveBeenCalledWith(
      expect.objectContaining({
        tenantId: 'tenant-1',
        customerId: 'customer-1',
        status: 'DRAFT',
        issueDate: '2026-07-03',
        dueDate: '2026-08-02',
        notes: 'PO 42',
        proposalId: 'proposal-1',
        subtotal: '11500.00',
        vatRate: '0.17',
        vatAmount: '1955.00',
        total: '13455.00',
      }),
    )
    expect(insertInvoiceLinesValues).toHaveBeenCalledWith([
      expect.objectContaining({
        description: 'Website Redesign',
        quantity: '2',
        unitPrice: '5000',
        discountPct: '10',
        lineTotal: '9000.00',
        position: 0,
      }),
      expect.objectContaining({
        description: 'Training',
        quantity: '1',
        unitPrice: '2500',
        discountPct: '0',
        lineTotal: '2500.00',
        position: 1,
      }),
    ])
    expect(result).toEqual({
      invoiceId: 'invoice-1',
      invoiceNumber: null,
    })
  })
})
