import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { execute } from '../execute.js'
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'

/**
 * Fixture project shape mirroring a SmartStack-generated web app:
 *   <root>/
 *     tsconfig.json
 *     src/
 *       components/ui/PageTemplate.tsx
 *       features/crm/contact/
 *         hooks/useContact.ts
 *         services/contactService.ts
 *         types/index.ts
 *       i18n/{fr,en,it,de}/crm/contact.json
 *       pages/myapp/crm/contacts/ContactListPage.tsx
 */

let projectPath: string

const TSCONFIG = JSON.stringify({
  compilerOptions: {
    paths: { '@/*': ['src/*'] },
  },
})

const PAGE_TEMPLATE = `export function PageTemplate({ children }: { children: any }) { return children }\n`
const HOOK_FILE = `export function useContact() { return { data: null } }\nexport function useDeleteContact() { return { mutateAsync: async () => {} } }\n`
const SERVICE_FILE = `export const contactService = {}\n`
const TYPES_FILE = `export interface ContactDetailDto { id: string }\n`
const LOCALE_JSON = JSON.stringify({
  list: { title: 'Contacts', columns: { firstName: 'First name' } },
  form: { fields: { lastName: 'Last name' } },
  breadcrumb: { section: 'Contacts' },
})

function setupProject(): string {
  const dir = mkdtempSync(join(tmpdir(), 'validate-page-'))
  writeFileSync(join(dir, 'tsconfig.json'), TSCONFIG)
  mkdirSync(join(dir, 'src/components/ui'), { recursive: true })
  writeFileSync(join(dir, 'src/components/ui/PageTemplate.tsx'), PAGE_TEMPLATE)
  mkdirSync(join(dir, 'src/features/crm/contact/hooks'), { recursive: true })
  mkdirSync(join(dir, 'src/features/crm/contact/services'), { recursive: true })
  mkdirSync(join(dir, 'src/features/crm/contact/types'), { recursive: true })
  writeFileSync(join(dir, 'src/features/crm/contact/hooks/useContact.ts'), HOOK_FILE)
  writeFileSync(join(dir, 'src/features/crm/contact/services/contactService.ts'), SERVICE_FILE)
  writeFileSync(join(dir, 'src/features/crm/contact/types/index.ts'), TYPES_FILE)
  for (const locale of ['fr', 'en', 'it', 'de']) {
    mkdirSync(join(dir, `src/i18n/${locale}/crm`), { recursive: true })
    writeFileSync(join(dir, `src/i18n/${locale}/crm/contact.json`), LOCALE_JSON)
  }
  mkdirSync(join(dir, 'src/pages/myapp/crm/contacts'), { recursive: true })
  return dir
}

function writePage(content: string, file = 'src/pages/myapp/crm/contacts/ContactListPage.tsx'): string {
  const abs = join(projectPath, file)
  writeFileSync(abs, content)
  return abs
}

beforeAll(() => {
  projectPath = setupProject()
})

afterAll(() => {
  if (projectPath) rmSync(projectPath, { recursive: true, force: true })
})

const HEALTHY_PAGE = `// @generated-by scaffold-component — DO NOT HAND-EDIT (re-run the CLI to update)
import { useTranslation } from 'react-i18next'
import { PermissionGuard } from '@atlashub/smartstack'
import { PageTemplate } from '@/components/ui/PageTemplate'
import { useContact } from '@/features/crm/contact/hooks/useContact'

export function ContactListPage() {
  const { t } = useTranslation('contact')
  const { data } = useContact()
  return (
    <PageTemplate title={t('list.title')} breadcrumbs={[{ label: t('breadcrumb.section') }]}>
      <PermissionGuard permission="crm.directory.create">
        <button>{t('list.columns.firstName')}</button>
      </PermissionGuard>
    </PageTemplate>
  )
}
`

describe('validate-page / execute — healthy page passes all rules', () => {
  it('returns zero violations on a fully-conformant page', () => {
    writePage(HEALTHY_PAGE)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.module).toBe('crm')
    expect(r.entity).toBe('Contact')
    expect(r.view).toBe('list')
    expect(r.violations).toEqual([])
  })
})

describe('validate-page / rule: cli-generated-marker', () => {
  it('flags a page that lacks the @generated-by header', () => {
    writePage(HEALTHY_PAGE.replace('// @generated-by scaffold-component — DO NOT HAND-EDIT (re-run the CLI to update)\n', ''))
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'cli-generated-marker')).toBe(true)
  })
})

describe('validate-page / rule: imports-resolve', () => {
  it('flags a relative import that does not resolve', () => {
    writePage(HEALTHY_PAGE.replace(
      "import { PageTemplate } from '@/components/ui/PageTemplate'",
      "import { PageTemplate } from '@/components/ui/MissingTemplate'",
    ))
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'imports-resolve')).toBe(true)
  })
})

describe('validate-page / rule: pagetemplate-wrapping', () => {
  it('flags a page that does not import PageTemplate from the canonical path', () => {
    writePage(`import { useTranslation } from 'react-i18next'\nexport function Page(){ return <div /> }\n`)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'pagetemplate-wrapping')).toBe(true)
  })
})

describe('validate-page / rule: useparams-null-check', () => {
  it('flags useParams<…>() without a null guard', () => {
    writePage(`import { useParams } from 'react-router-dom'
import { PageTemplate } from '@/components/ui/PageTemplate'
import { useContact } from '@/features/crm/contact/hooks/useContact'
export function ContactListPage(){
  const { id } = useParams<{ id: string }>()
  const { data } = useContact(id)
  return <PageTemplate title="x">{data?.id}</PageTemplate>
}
`)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'useparams-null-check')).toBe(true)
  })

  it('passes when the guard is present', () => {
    writePage(`import { useParams, Navigate } from 'react-router-dom'
import { PageTemplate } from '@/components/ui/PageTemplate'
import { useContact } from '@/features/crm/contact/hooks/useContact'
export function ContactListPage(){
  const { id } = useParams<{ id: string }>()
  if (!id) return <Navigate to=".." replace />
  const { data } = useContact(id)
  return <PageTemplate title="x">{data?.id}</PageTemplate>
}
`)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'useparams-null-check')).toBe(false)
  })
})

describe('validate-page / rule: permissionguard-on-mutations', () => {
  it('flags a mutateAsync call not wrapped in PermissionGuard', () => {
    writePage(`import { PageTemplate } from '@/components/ui/PageTemplate'
import { useDeleteContact } from '@/features/crm/contact/hooks/useContact'
export function ContactListPage(){
  const del = useDeleteContact()
  return <PageTemplate title="x"><button onClick={() => del.mutateAsync('1')}>delete</button></PageTemplate>
}
`)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'permissionguard-on-mutations')).toBe(true)
  })
})

describe('validate-page / rule: no-local-usepermissions', () => {
  it('flags a local function usePermissions() definition', () => {
    writePage(`import { PageTemplate } from '@/components/ui/PageTemplate'
function usePermissions() { return { canEdit: true } }
export function ContactListPage(){
  const { canEdit } = usePermissions()
  return <PageTemplate title="x">{canEdit ? 'yes' : 'no'}</PageTemplate>
}
`)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'no-local-usepermissions')).toBe(true)
  })

  it('flags a const usePermissions = … definition', () => {
    writePage(`import { PageTemplate } from '@/components/ui/PageTemplate'
const usePermissions = () => ({ canEdit: true })
export function ContactListPage(){
  return <PageTemplate title="x">x</PageTemplate>
}
`)
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'no-local-usepermissions')).toBe(true)
  })
})

describe('validate-page / rule: i18n-keys-resolve', () => {
  it('flags a t() key missing in some locale files', () => {
    writePage(HEALTHY_PAGE.replace("t('list.title')", "t('list.titleMissingEverywhere')"))
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    const i18nViolations = r.violations.filter((v) => v.rule === 'i18n-keys-resolve')
    expect(i18nViolations.length).toBe(4) // one per locale
  })
})

describe('validate-page / rule: hook-imports-exist', () => {
  it('flags a named import that the target file does not export', () => {
    writePage(HEALTHY_PAGE.replace(
      "import { useContact } from '@/features/crm/contact/hooks/useContact'",
      "import { useDashboardContact } from '@/features/crm/contact/hooks/useContact'",
    ).replace('useContact()', 'useDashboardContact()'))
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'hook-imports-exist' && v.message.includes('useDashboardContact'))).toBe(true)
  })
})

describe('validate-page / rule: permission-keys-no-appcode', () => {
  it('flags a 4-segment permission key (appCode prefix)', () => {
    writePage(HEALTHY_PAGE.replace('crm.directory.create', 'testv2.crm.directory.create'))
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'permission-keys-no-appcode')).toBe(true)
  })

  it('flags a 2-segment permission key (missing action)', () => {
    writePage(HEALTHY_PAGE.replace('crm.directory.create', 'crm.directory'))
    const r = execute({ projectPath, pageFile: 'src/pages/myapp/crm/contacts/ContactListPage.tsx' })
    expect(r.violations.some((v) => v.rule === 'permission-keys-no-appcode')).toBe(true)
  })
})
