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

const mockListTemplates = vi.fn()
const mockInstantiateTemplate = vi.fn()
const mockSaveProjectAsTemplate = vi.fn()

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

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

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

vi.mock('@zync/db/queries', () => ({
  listTemplates: (...args: unknown[]) => mockListTemplates(...args),
  instantiateTemplate: (...args: unknown[]) => mockInstantiateTemplate(...args),
  saveProjectAsTemplate: (...args: unknown[]) => mockSaveProjectAsTemplate(...args),
  instantiateTemplateSchema: {
    safeParse: vi.fn((body: unknown) => {
      if (
        typeof body === 'object' &&
        body !== null &&
        typeof (body as { template_id?: unknown }).template_id === 'string' &&
        typeof (body as { project?: { name?: unknown; start_date?: unknown; billing_type?: unknown } }).project?.name === 'string' &&
        typeof (body as { project?: { start_date?: unknown } }).project?.start_date === 'string' &&
        typeof (body as { project?: { billing_type?: unknown } }).project?.billing_type === 'string'
      ) {
        return { success: true, data: body }
      }
      return { success: false, error: { issues: [{ path: ['project', 'start_date'], message: 'Required' }] } }
    }),
  },
  saveAsTemplateSchema: {
    safeParse: vi.fn((body: unknown) => {
      if (typeof body === 'object' && body !== null && typeof (body as { name?: unknown }).name === 'string') {
        return { success: true, data: body }
      }
      return { success: false, error: { issues: [{ path: ['name'], message: 'Required' }] } }
    }),
  },
  getTemplate: vi.fn(),
  getTemplateTasks: vi.fn(),
  createTemplate: vi.fn(),
  updateTemplate: vi.fn(),
  deleteTemplate: vi.fn(),
  addTemplateTask: vi.fn(),
  updateTemplateTask: vi.fn(),
  deleteTemplateTask: vi.fn(),
  createTemplateSchema: { safeParse: vi.fn() },
  updateTemplateSchema: { safeParse: vi.fn() },
  createTemplateTaskSchema: { safeParse: vi.fn() },
  updateTemplateTaskSchema: { safeParse: vi.fn() },
}))

import { projectTemplatesRoute } from '../src/routes/project-templates/index'
import { projectRoutes } from '../src/routes/projects'

function appForProjects(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', projectRoutes)
  return app
}

function appForTemplates(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/project-templates', projectTemplatesRoute)
  return app
}

describe('project template route surface', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockListTemplates.mockResolvedValue({ items: [], total: 0, page: 2, pageSize: 5 })
    mockInstantiateTemplate.mockResolvedValue({ project_id: 'project-1', tasks_created: 2 })
    mockSaveProjectAsTemplate.mockResolvedValue({ id: 'template-1' })
  })

  it('forwards list filters and pagination to the repo', async () => {
    const res = await appForTemplates({
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      permissions: ['projects:read'],
    }).request('/api/project-templates?category=design&q=web&page=2&pageSize=5', undefined, {} as AppEnv['Bindings'])

    expect(res.status).toBe(200)
    expect(mockListTemplates).toHaveBeenCalledWith({}, 'tenant-1', {
      category: 'design',
      q: 'web',
      page: 2,
      pageSize: 5,
    })
  })

  it('mounts POST /api/projects/from-template', async () => {
    const res = await appForProjects({
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      permissions: ['projects:write'],
    }).request('/api/projects/from-template', {
      method: 'POST',
      body: JSON.stringify({
        template_id: '11111111-1111-1111-1111-111111111111',
        project: {
          name: 'Website redesign',
          start_date: '2026-07-01',
          billing_type: 'fixed',
        },
      }),
      headers: { 'content-type': 'application/json' },
    }, {} as AppEnv['Bindings'])

    expect(res.status).toBe(201)
    expect(mockInstantiateTemplate).toHaveBeenCalledWith(
      {},
      'tenant-1',
      expect.objectContaining({ template_id: '11111111-1111-1111-1111-111111111111' }),
      'user-1',
    )
  })

  it('returns 400 for invalid create-from-template bodies', async () => {
    const res = await appForProjects({
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      permissions: ['projects:write'],
    }).request('/api/projects/from-template', {
      method: 'POST',
      body: JSON.stringify({
        template_id: '11111111-1111-1111-1111-111111111111',
        project: { name: 'Website redesign', billing_type: 'fixed' },
      }),
      headers: { 'content-type': 'application/json' },
    }, {} as AppEnv['Bindings'])

    expect(res.status).toBe(400)
    expect(mockInstantiateTemplate).not.toHaveBeenCalled()
  })

  it('mounts POST /api/projects/:id/save-as-template', async () => {
    const res = await appForProjects({
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      permissions: ['projects:write'],
    }).request('/api/projects/project-1/save-as-template', {
      method: 'POST',
      body: JSON.stringify({ name: 'Client onboarding', category: 'consulting' }),
      headers: { 'content-type': 'application/json' },
    }, {} as AppEnv['Bindings'])

    expect(res.status).toBe(201)
    expect(mockSaveProjectAsTemplate).toHaveBeenCalledWith(
      {},
      'tenant-1',
      'project-1',
      { name: 'Client onboarding', category: 'consulting' },
      'user-1',
    )
  })
})
