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

const mockGetTenantSettings = vi.fn()
const mockGetTierQuotas = vi.fn()
const mockUpsertTenantSettings = vi.fn()
const mockTenantUsageBreakdown = vi.fn()
const mockGetCreditPurchases = vi.fn()
const mockGetGlobalConfig = vi.fn()
const mockGetModelPricing = vi.fn()
const mockGetQuotaStatus = vi.fn()
const mockUsdToTokens = vi.fn()

const session: SessionPayload = {
  sub: '00000000-0000-4000-8000-000000000001',
  tid: '00000000-0000-4000-8000-000000000002',
  role: 'OWNER',
  permissions: ['settings:write'],
  tier: TenantTier.BUSINESS,
  type: 'user',
  v: 1,
  enforce_2fa: false,
  two_factor_verified: false,
}

vi.mock('@zync/db/queries', () => ({
  createDb: vi.fn(() => ({})),
  getTenantSettings: (...args: unknown[]) => mockGetTenantSettings(...args),
  upsertTenantSettings: (...args: unknown[]) => mockUpsertTenantSettings(...args),
  getCreditPurchases: (...args: unknown[]) => mockGetCreditPurchases(...args),
  tenantUsageBreakdown: (...args: unknown[]) => mockTenantUsageBreakdown(...args),
  getTierQuotas: (...args: unknown[]) => mockGetTierQuotas(...args),
  getModelPricing: (...args: unknown[]) => mockGetModelPricing(...args),
  getGlobalConfig: (...args: unknown[]) => mockGetGlobalConfig(...args),
}))

vi.mock('@zync/ai', () => ({
  getQuotaStatus: (...args: unknown[]) => mockGetQuotaStatus(...args),
  usdToTokens: (...args: unknown[]) => mockUsdToTokens(...args),
  AI_USE_CASE_LABELS: {
    expense_ocr: 'Expense OCR',
    expense_tax_eval: 'Expense Tax Evaluation',
    task_autocreate: 'Task Auto-Create',
    ai_assistant: 'AI Assistant',
    telegram_assistant: 'Telegram Assistant',
    kb_suggest: 'Knowledge Base Suggestions',
    invoice_extract: 'Invoice Extraction',
  },
}))

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

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

import { aiRoutes } from '../src/routes/ai'

function appWithRoutes() {
  const app = new Hono<AppEnv>()
  app.route('/api/ai', aiRoutes)
  return app
}

describe('system-ai tenant routes', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockGetTenantSettings.mockResolvedValue({
      tenantId: session.tid,
      personalityPrompt: null,
      useCaseOverrides: {},
      extraUsageEnabled: false,
      extraSpendLimitUsd: null,
      autoReloadEnabled: false,
      autoReloadAmountUsd: null,
      updatedAt: new Date(),
    })
    mockGetTierQuotas.mockResolvedValue([
      {
        tier: TenantTier.BUSINESS,
        extraAllowed: true,
        extraMaxUsd: '25.00',
      },
    ])
    mockGetCreditPurchases.mockResolvedValue([])
    mockGetGlobalConfig.mockResolvedValue({
      mainModel: { model: 'gpt-4o', provider: 'openai', label: 'GPT-4o' },
    })
    mockGetModelPricing.mockResolvedValue({
      inputCostPer1m: 2.5,
      outputCostPer1m: 10,
    })
    mockGetQuotaStatus.mockResolvedValue({
      percentUsed: 45,
      tokensUsed: 450,
      tokensTotal: 1000,
      tokensRemaining: 550,
      resetsAt: '2026-08-01',
    })
    mockUsdToTokens.mockResolvedValue(1_000_000)
  })

  it('persists purchased-credit auto-reload settings via PUT /api/ai/settings', async () => {
    const res = await appWithRoutes().request('/api/ai/settings', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        autoReloadEnabled: true,
        autoReloadAmountUsd: 25,
      }),
    })

    expect(res.status).toBe(200)
    expect(mockUpsertTenantSettings).toHaveBeenCalledWith(
      {},
      session.tid,
      expect.objectContaining({
        autoReloadEnabled: true,
        autoReloadAmountUsd: '25.00',
      }),
    )
  })

  it('returns tenant usage rows with display labels for the settings dashboard', async () => {
    mockTenantUsageBreakdown.mockResolvedValue([
      {
        useCase: 'expense_ocr',
        calls: 42,
        totalTokens: 1234,
        percentOfUsage: 38,
      },
    ])

    const res = await appWithRoutes().request('/api/ai/usage?range=month')

    expect(res.status).toBe(200)
    const body = (await res.json()) as {
      breakdown: Array<{ useCase: string; label?: string }>
    }
    expect(body.breakdown).toEqual([
      expect.objectContaining({
        useCase: 'expense_ocr',
        label: 'Expense OCR',
      }),
    ])
  })
})
