/**
 * hub-views.test.ts — Locks the contract for non-CRUD pagespec views (Wave 1
 * of the URL-convention alignment, 2026-05-27).
 *
 * Hub views (app-home / module-home / section-home) are pure Slot containers
 * on the frontend — scaffold-component emits NO useService hook and NO
 * api.get/post call. The screen controller therefore emits NO endpoint and
 * NO TODO marker for them: the absence is by design, not technical debt.
 *
 * Kanban / card views read the same data set as `list` (different
 * visualisation, same projection) — they fall back to the [HttpGet("list")]
 * endpoint and push a TODO so audit-dev-api can track the pending
 * dedicated emitter.
 */
import { describe, it, expect } from 'vitest'
import { generate } from '../generate.js'
import type { PageSpec, ScaffoldScreenControllerSpec } from '../types.js'

function spec(overrides: Partial<ScaffoldScreenControllerSpec> = {}): ScaffoldScreenControllerSpec {
  return {
    section: 'accueil',
    entity: 'Demande',
    module: 'affaires',
    appCode: 'gaf',
    namespace: 'Gaf',
    moduleDir: '/ba/affaires',
    projectPath: '/api',
    ...overrides,
  } as ScaffoldScreenControllerSpec
}

function pagespec(view: string, overrides: Partial<PageSpec> = {}): PageSpec {
  return {
    screenCode: `SCR-AFFAIRES-DEMANDES-${view.toUpperCase()}`,
    appCode: 'gaf',
    module: 'affaires',
    section: 'accueil',
    entity: 'Demande',
    view: view as PageSpec['view'],
    permission: 'affaires.demandes.read',
    linkedUseCases: [],
    linkedBusinessRules: [],
    columns: [],
    actions: [],
    ...overrides,
  } as PageSpec
}

function controllerOf(files: { path: string; content: string }[]): { path: string; content: string } | undefined {
  return files.find((f) => f.path.endsWith('ScreenController.cs'))
}

describe('scaffold-screen-controller — hub views (no endpoint, no debt)', () => {
  for (const view of ['app-home', 'module-home', 'section-home'] as const) {
    it(`view "${view}" emits NO method, NO DTO, NO TODO`, () => {
      const { files, todos } = generate(spec(), [pagespec(view)])

      // The controller scaffolding still emits the class shell (so other
      // pagespecs of the same entity can attach to it), but the body must
      // not carry the view's CRUD endpoints.
      const ctrl = controllerOf(files)
      expect(ctrl).toBeDefined()
      expect(ctrl!.content).not.toMatch(/\[HttpGet\("list"\)\]/)
      expect(ctrl!.content).not.toMatch(/\[HttpGet\("detail/)
      expect(ctrl!.content).not.toMatch(/\[HttpPost\("form"\)\]/)
      expect(ctrl!.content).not.toMatch(/\[HttpGet\("dashboard\/consolidated"\)\]/)

      // No DTO file emitted for hub views.
      expect(files.find((f) => f.path.includes('ListScreenDto'))).toBeUndefined()
      expect(files.find((f) => f.path.includes('DetailScreenDto'))).toBeUndefined()

      // No TODO — the absence of an endpoint is intentional, not debt.
      expect(todos).toHaveLength(0)
    })
  }

  it('hub view + sibling list view → ONE controller with the list endpoint only', () => {
    const { files } = generate(spec(), [
      pagespec('section-home'),
      pagespec('list', { screenCode: 'SCR-AFFAIRES-DEMANDES-LIST' }),
    ])
    const ctrl = controllerOf(files)!
    expect(ctrl.content).toMatch(/\[HttpGet\("list"\)\]/)
    // The hub did not inject anything extra.
    expect(ctrl.content).not.toMatch(/HomeDto/)
  })
})

describe('scaffold-screen-controller — kanban / card fallback to list', () => {
  for (const view of ['kanban', 'card'] as const) {
    it(`view "${view}" reuses the list endpoint + DTO and tracks a TODO`, () => {
      const { files, todos } = generate(spec(), [pagespec(view)])
      const ctrl = controllerOf(files)!
      expect(ctrl.content).toMatch(/\[HttpGet\("list"\)\]/)
      expect(ctrl.content).toMatch(/PaginatedResult<DemandeListScreenDto>/)

      // A list DTO must be emitted because the controller method references it.
      const dto = files.find((f) => f.path.endsWith('DemandeListScreenDto.cs'))
      expect(dto).toBeDefined()

      // One TODO entry signalling the pending dedicated emitter.
      expect(todos.some((t) => t.includes(view) && t.includes('MVP fallback'))).toBe(true)
    })
  }
})
