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

const TENANT_ID = '00000000-0000-4000-8000-000000000001'
const USER_ID = '00000000-0000-4000-8000-000000000099'

const mockListEntries = vi.fn()
const mockListUnbilledTimeEntries = vi.fn()

let mockSession: Record<string, unknown> = {
  type: 'user',
  sub: USER_ID,
  tid: TENANT_ID,
  role: 'ADMIN',
  permissions: ['time:read', 'time:write', 'time:read_all'],
}

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

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (
    c: { set: (key: string, value: unknown) => void },
    next: () => Promise<void>,
  ) => {
    c.set('db', {})
    c.set('session', mockSession)
    await next()
  },
}))

vi.mock('../src/middleware/require-module-enabled', () => ({
  requireModuleEnabled: () => async (_c: unknown, next: () => Promise<void>) => {
    await next()
  },
}))

vi.mock('@zync/time', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@zync/time')>()
  return {
    ...actual,
    listEntries: (...args: unknown[]) => mockListEntries(...args),
  }
})

vi.mock('@zync/db/queries', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@zync/db/queries')>()
  return {
    ...actual,
    listUnbilledTimeEntries: (...args: unknown[]) => mockListUnbilledTimeEntries(...args),
  }
})

import { timeRoutes } from '../src/routes/time'

function appWithTimeRoutes() {
  const app = new Hono<AppEnv>()
  app.route('/api/time', timeRoutes)
  return app
}

describe('time-to-invoice time routes', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockSession = {
      type: 'user',
      sub: USER_ID,
      tid: TENANT_ID,
      role: 'ADMIN',
      permissions: ['time:read', 'time:write', 'time:read_all'],
    }
    mockListEntries.mockResolvedValue({ items: [], nextCursor: null, total: 0 })
    mockListUnbilledTimeEntries.mockResolvedValue({ entries: [], nextCursor: null })
  })

  it('routes unbilled requests through the time-to-invoice helper', async () => {
    const app = appWithTimeRoutes()
    const res = await app.request(
      '/api/time?unbilled=true&projectId=00000000-0000-4000-8000-000000000123&customerId=00000000-0000-4000-8000-000000000124&from=2026-06-01&to=2026-06-30',
    )

    expect(res.status).toBe(200)
    expect(mockListUnbilledTimeEntries).toHaveBeenCalledWith(
      expect.anything(),
      expect.objectContaining({
        tenantId: TENANT_ID,
        projectId: '00000000-0000-4000-8000-000000000123',
        customerId: '00000000-0000-4000-8000-000000000124',
        userId: USER_ID,
        dateFrom: '2026-06-01',
        dateTo: '2026-06-30',
      }),
    )
    expect(mockListEntries).not.toHaveBeenCalled()
  })

  it('keeps the normal list path for non-unbilled requests', async () => {
    const app = appWithTimeRoutes()
    const res = await app.request('/api/time?projectId=00000000-0000-4000-8000-000000000123')

    expect(res.status).toBe(200)
    expect(mockListEntries).toHaveBeenCalled()
    expect(mockListUnbilledTimeEntries).not.toHaveBeenCalled()
  })
})
