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

const mockGetProjectAnalytics = vi.fn()

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

vi.mock('@zync/db/queries', () => ({
  getProjectAnalytics: (...args: unknown[]) => mockGetProjectAnalytics(...args),
}))

import { projectAnalyticsRoutes } from '../src/routes/projects/analytics'

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/projects', projectAnalyticsRoutes)
  return app
}

describe('project analytics route', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('passes weeks plus member-scoped visibility and omits financials without invoices permission', async () => {
    mockGetProjectAnalytics.mockResolvedValue({
      time: {
        total_hours: 127,
        this_week_hours: 12,
        last_week_hours: 18,
        by_member: [],
        weekly_trend: [],
      },
      tasks: { total: 42, done: 38, open: 4 },
    })

    const res = await appFor({
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      role: 'MEMBER',
      tier: 'freelancer',
      permissions: ['projects:read'],
    }).request('/api/projects/project-1/analytics?weeks=6')

    expect(res.status).toBe(200)
    expect(mockGetProjectAnalytics).toHaveBeenCalledWith({}, 'tenant-1', 'project-1', {
      access: { fullVisibility: false, userId: 'user-1' },
      includeFinancials: false,
      tier: 'freelancer',
      weeks: 6,
    })
    await expect(res.json()).resolves.toEqual({
      analytics: {
        time: {
          total_hours: 127,
          this_week_hours: 12,
          last_week_hours: 18,
          by_member: [],
          weekly_trend: [],
        },
        tasks: { total: 42, done: 38, open: 4 },
      },
    })
  })

  it('includes financials for Business+ users with invoices:read', async () => {
    mockGetProjectAnalytics.mockResolvedValue({
      time: {
        total_hours: 127,
        this_week_hours: 12,
        last_week_hours: 18,
        by_member: [],
        weekly_trend: [],
      },
      tasks: { total: 42, done: 38, open: 4 },
      financials: {
        invoiced_total: '28400.00',
        collected_total: '24200.00',
        outstanding_total: '4200.00',
        invoice_count: 6,
        expenses_total: '2100.00',
        expense_count: 8,
        team_cost: '19050.00',
        gross_margin: '7250.00',
        gross_margin_pct: 25.53,
      },
    })

    const res = await appFor({
      type: 'user',
      sub: 'owner-1',
      tid: 'tenant-1',
      role: 'OWNER',
      tier: 'business',
      permissions: ['projects:read', 'invoices:read'],
    }).request('/api/projects/project-1/analytics')

    expect(res.status).toBe(200)
    expect(mockGetProjectAnalytics).toHaveBeenCalledWith({}, 'tenant-1', 'project-1', {
      access: { fullVisibility: true, userId: 'owner-1' },
      includeFinancials: true,
      tier: 'business',
      weeks: 8,
    })
  })

  it('returns 404 when the project is missing', async () => {
    mockGetProjectAnalytics.mockResolvedValue(null)

    const res = await appFor({
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      role: 'MEMBER',
      tier: 'business',
      permissions: ['projects:read', 'invoices:read'],
    }).request('/api/projects/project-404/analytics')

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