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

const mockGetTenantProfitability = vi.fn()
const mockGetProjectProfitability = vi.fn()
const mockGetCustomerProfitability = vi.fn()

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (_c: unknown, next: () => Promise<void>) => next(),
}))

vi.mock('../src/middleware/guards', () => ({
  requirePermission: () => async (_c: unknown, next: () => Promise<void>) => next(),
  requireTier: () => async (_c: unknown, next: () => Promise<void>) => next(),
}))

vi.mock('@zync/db/queries', () => ({
  getTenantProfitability: (...args: unknown[]) => mockGetTenantProfitability(...args),
  getProjectProfitability: (...args: unknown[]) => mockGetProjectProfitability(...args),
  getCustomerProfitability: (...args: unknown[]) => mockGetCustomerProfitability(...args),
}))

import { profitabilityRoutes } from '../src/routes/reports/profitability'

function appFor(session: Record<string, unknown>) {
  const app = new Hono<AppEnv>()
  app.use('*', async (c, next) => {
    c.set('session', session)
    c.set('db', {})
    await next()
  })
  app.route('/api/reports/profitability', profitabilityRoutes)
  return app
}

describe('profitability routes', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('returns tenant summary with by-project and by-customer lists', async () => {
    mockGetTenantProfitability.mockResolvedValue({
      from: '2026-01-01',
      to: '2026-06-30',
      summary: { revenue: '100.00', cost: '40.00', profit: '60.00', margin: '60.00' },
      byProject: [],
      byCustomer: [],
    })

    const res = await appFor({
      type: 'user',
      tid: 'tenant-1',
      role: 'OWNER',
      tier: 'business',
      permissions: ['reports:read'],
    }).request('/api/reports/profitability?from=2026-01-01&to=2026-06-30')

    expect(res.status).toBe(200)
    expect(mockGetTenantProfitability).toHaveBeenCalledWith({}, 'tenant-1', '2026-01-01', '2026-06-30')
    await expect(res.json()).resolves.toEqual({
      from: '2026-01-01',
      to: '2026-06-30',
      summary: { revenue: '100.00', cost: '40.00', profit: '60.00', margin: '60.00' },
      byProject: [],
      byCustomer: [],
    })
  })

  it('rejects members even when they have reports access', async () => {
    const res = await appFor({
      type: 'user',
      tid: 'tenant-1',
      role: 'MEMBER',
      tier: 'business',
      permissions: ['reports:read'],
    }).request('/api/reports/profitability')

    expect(res.status).toBe(403)
    await expect(res.json()).resolves.toEqual({ error: 'Forbidden — OWNER or ADMIN only' })
  })

  it('uses the customers breakdown path from the spec', async () => {
    mockGetCustomerProfitability.mockResolvedValue({
      customerId: 'customer-1',
      customerName: 'Acme Corp',
      projects: [],
      summary: { revenue: '0.00', cost: '0.00', profit: '0.00', margin: null },
    })

    const res = await appFor({
      type: 'user',
      tid: 'tenant-1',
      role: 'ADMIN',
      tier: 'business',
      permissions: ['reports:read'],
    }).request('/api/reports/profitability/customers/customer-1')

    expect(res.status).toBe(200)
    expect(mockGetCustomerProfitability).toHaveBeenCalledWith({}, 'tenant-1', 'customer-1')
  })

  it('returns 404 when a customer breakdown is missing', async () => {
    mockGetCustomerProfitability.mockResolvedValue(null)

    const res = await appFor({
      type: 'user',
      tid: 'tenant-1',
      role: 'ADMIN',
      tier: 'business',
      permissions: ['reports:read'],
    }).request('/api/reports/profitability/customers/customer-404')

    expect(res.status).toBe(404)
    await expect(res.json()).resolves.toEqual({ error: 'Customer not found' })
  })
})
