import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Hono } from 'hono'
import type { AppEnv } from '../src/types'

const mockGetInvoiceSettings = vi.fn()
const mockUpdateInvoiceSettings = vi.fn()

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (
    c: { set: (key: string, value: unknown) => void },
    next: () => Promise<void>,
  ) => {
    c.set('session', {
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      permissions: ['settings:read', 'settings:write'],
    })
    await next()
  },
}))

vi.mock('../src/middleware/guards', () => ({
  requirePermission: (permission: string) => {
    return async (
      c: {
        get: (key: string) => { permissions?: string[] } | undefined
        json: (body: unknown, status: number) => Response
      },
      next: () => Promise<void>,
    ) => {
      const session = c.get('session')
      if (!session?.permissions?.includes(permission)) {
        return c.json({ error: 'Forbidden' }, 403)
      }
      await next()
    }
  },
}))

vi.mock('@zync/db/queries', () => ({
  createDb: vi.fn(() => ({})),
  getInvoiceSettings: (...args: unknown[]) => mockGetInvoiceSettings(...args),
  updateInvoiceSettings: (...args: unknown[]) => mockUpdateInvoiceSettings(...args),
}))

import { invoicingSettingsRoute } from '../src/routes/settings/invoicing'

function app() {
  const instance = new Hono<AppEnv>()
  instance.route('/api/settings/invoicing', invoicingSettingsRoute)
  return instance
}

describe('/api/settings/invoicing', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockGetInvoiceSettings.mockResolvedValue({
      default_payment_terms_days: 30,
      default_tax_rate: 0.18,
      default_currency: 'ILS',
      invoice_number_prefix: 'INV-',
      issue_tax_invoices: true,
      proforma_number_prefix: 'PROFORMA-',
      late_fee_type: 'none',
      late_fee_amount: null,
      late_fee_threshold_days: 30,
      invoice_footer_text: null,
      invoice_show_payment_link: true,
      business_type: 'osek_morshe',
      business_tax_id: '123',
      vat_registration_number: '456',
      logo_url: null,
    })
    mockUpdateInvoiceSettings.mockResolvedValue({
      default_payment_terms_days: 45,
      default_tax_rate: 0.18,
      default_currency: 'USD',
      invoice_number_prefix: 'INV-',
      issue_tax_invoices: true,
      proforma_number_prefix: 'PROFORMA-',
      late_fee_type: 'flat',
      late_fee_amount: 50,
      late_fee_threshold_days: 30,
      invoice_footer_text: null,
      invoice_show_payment_link: true,
      business_type: 'osek_morshe',
      business_tax_id: '123',
      vat_registration_number: '456',
      logo_url: null,
    })
  })

  it('returns the canonical invoicing settings payload on GET', async () => {
    const res = await app().request('/api/settings/invoicing')

    expect(res.status).toBe(200)
    await expect(res.json()).resolves.toEqual(
      expect.objectContaining({
        default_payment_terms_days: 30,
        default_tax_rate: 0.18,
        invoice_show_payment_link: true,
      }),
    )
  })

  it('validates late-fee cross-field rules on PATCH', async () => {
    const res = await app().request('/api/settings/invoicing', {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ late_fee_type: 'flat', late_fee_amount: null }),
    })

    expect(res.status).toBe(422)
    await expect(res.json()).resolves.toEqual(
      expect.objectContaining({
        error: 'Validation failed',
      }),
    )
    expect(mockUpdateInvoiceSettings).not.toHaveBeenCalled()
  })

  it('persists partial patches against the canonical endpoint', async () => {
    const res = await app().request('/api/settings/invoicing', {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        default_payment_terms_days: 45,
        default_currency: 'USD',
        late_fee_type: 'flat',
        late_fee_amount: 50,
      }),
    })

    expect(res.status).toBe(200)
    expect(mockUpdateInvoiceSettings).toHaveBeenCalledWith(
      {},
      'tenant-1',
      'user-1',
      expect.objectContaining({
        default_payment_terms_days: 45,
        default_currency: 'USD',
        late_fee_type: 'flat',
        late_fee_amount: 50,
      }),
    )
  })
})
