// retoor <retoor@molodetz.nl>
export const meta = {
  name: 'endpoint',
  description: 'Scaffold ONE new DevPlace route across all of its touchpoints (Form model, Out schema, guarded handler with respond, main.py mount, template, Devii action, API docs, SEO) and verify it, then write its integration test in the matching tier (api for JSON/HTML, e2e for an interactive UI flow)',
  phases: [
    { title: 'Understand', detail: 'find the closest existing route to mirror' },
    { title: 'Implement', detail: 'wire the route across every touchpoint' },
    { title: 'Verify', detail: 'completeness and security review of the new route' },
    { title: 'Fix', detail: 'close gaps from the review' },
    { title: 'Test', detail: 'write the route test in the matching tier (api or e2e), mirroring the path' },
  ],
}

const RULES = [
  'Obey DevPlace hard rules while editing:',
  '- No comments or docstrings in source (except the file header and @tool docstrings). New files start with the "retoor <retoor@molodetz.nl>" header in the language comment style.',
  '- No em-dash characters; use a hyphen. Full Python type hints; pathlib over os; Pydantic Form input with explicit max lengths; sanitize and bound user input.',
  '- Reuse templating.templates, database.py batch helpers, respond(), the shared partials and frontend utilities. Never per-router Jinja2Templates.',
  '- Guards: get_current_user (public read), require_user (member write), require_admin (admin). Every POST/PUT/DELETE is guarded. Declare specific routes before catch-alls. Never pass a respond() context key that collides with a Jinja global (use viewer_is_admin).',
  '- Validate with the per-language checks (py_compile + pyflakes for Python, node --check for JS, brace balance for CSS, tag and {% %} balance for templates) and "python -c \\"from devplacepy.main import app\\"". NEVER run the test suite. Never perform any git write.',
].join('\n')

const TOUCHPOINTS = [
  'A single DevPlace route must be wired across these touchpoints, all in agreement:',
  '1. models.py - a Form model for the input (data: Annotated[SomeForm, Form()]) with max lengths, if it takes a body.',
  '2. schemas.py - a *Out(_Out) model carrying every key the JSON response returns.',
  '3. database.py - any query/batch helper it needs (no inline N+1); indexes in init_db() if it queries a new column.',
  '4. routers/{area}.py - the handler with the correct guard, returning respond(request, template, ctx, model=XOut); register the router in main.py with its prefix if new.',
  '5. templates/ + static/css + static/js - the view if it renders HTML.',
  '6. services/devii/actions/catalog.py - an Action whose method/path/requires_auth/requires_admin match the route guard, if a user could ask Devii to do it; confirm param + CONFIRM_REQUIRED if destructive.',
  '7. docs_api.py - an endpoint() entry with params and sample_response.',
  '8. seo.py - base_seo_context for a public page; sitemap entry if indexable.',
].join('\n')

const TESTS = [
  'DevPlace test standard (a hard project requirement - one test file per endpoint, the directory tree mirroring the URL path):',
  '- tests/api/ - HTTP integration test against the live uvicorn subprocess (app_server/seeded_db, requests/httpx vs BASE_URL) - the right tier for a JSON or HTML route.',
  '- tests/e2e/ - Playwright browser test (page/alice/bob) - the right tier for an interactive UI flow.',
  'The route path maps to the test path by dropping {param} segments and lowercasing each segment (POST /auth/login -> tests/api/auth/login.py; GET /admin/ai-usage -> tests/e2e/admin/aiusage.py). A collection path that also parents deeper paths uses index.py in its own directory. Create any missing package directories with __init__.py.',
  'Required patterns: wait_until="domcontentloaded" on every goto/wait_for_url; scoped selectors; try/finally restore of any flipped global setting; the shared fixtures; test FUNCTIONS are test_-prefixed though files are not. Validate by a clean import only. NEVER run the suite.',
].join('\n')

function endpointBrief() {
  if (!args) return ''
  if (typeof args === 'string') return args
  if (typeof args.description === 'string') return args.description
  return JSON.stringify(args)
}

const ask = endpointBrief()
if (!ask) {
  log('No endpoint description provided. Invoke as /endpoint <method path - purpose>.')
  return { error: 'no description provided' }
}

const MAP_SCHEMA = {
  type: 'object',
  additionalProperties: false,
  required: ['summary'],
  properties: {
    summary: { type: 'string' },
    similarRoute: { type: 'string' },
    files: { type: 'array', items: { type: 'string' } },
  },
}

const BUILD_SCHEMA = {
  type: 'object',
  additionalProperties: false,
  required: ['summary', 'filesChanged', 'validatorPassed'],
  properties: {
    summary: { type: 'string' },
    filesChanged: { type: 'array', items: { type: 'string' } },
    validatorPassed: { type: 'boolean' },
    importOk: { type: 'boolean' },
  },
}

const FINDINGS_SCHEMA = {
  type: 'object',
  additionalProperties: false,
  required: ['summary', 'findings'],
  properties: {
    summary: { type: 'string' },
    findings: {
      type: 'array',
      items: {
        type: 'object',
        additionalProperties: false,
        required: ['severity', 'file', 'rule', 'message'],
        properties: {
          severity: { type: 'string', enum: ['error', 'warning', 'info'] },
          file: { type: 'string' },
          line: { type: 'integer' },
          rule: { type: 'string' },
          message: { type: 'string' },
        },
      },
    },
  },
}

log(`Endpoint: ${ask}`)

const map = await agent(
  `Find the closest existing DevPlace route to mirror for this new endpoint, and read it end to end (handler, schema, docs entry, Devii action, test). Do not write anything.\n\nEndpoint: ${ask}\n\n${TOUCHPOINTS}`,
  { agentType: 'Explore', label: 'understand', phase: 'Understand', schema: MAP_SCHEMA }
)

const build = await agent(
  `Implement this single DevPlace route across every applicable touchpoint, editing files directly in the repo, mirroring the closest existing route. Keep the layers in agreement (Out schema carries every returned JSON key; Devii action auth flags match the guard). Then run the per-language checks (py_compile + pyflakes for Python, node --check for JS, brace balance for CSS, tag and {% %} balance for templates) and "python -c \\"from devplacepy.main import app\\"". Do not write the test here. Do not run the suite. Do not commit.\n\nEndpoint: ${ask}\n\nClosest route to mirror:\n${JSON.stringify(map, null, 2)}\n\n${TOUCHPOINTS}\n\n${RULES}\n\nReturn the files changed and whether the checks and the import passed.`,
  { label: 'implement', phase: 'Implement', schema: BUILD_SCHEMA }
)

const changed = (build && build.filesChanged) || []
const scopeNote = changed.length ? `\n\nRestrict findings to these files:\n${changed.join('\n')}` : ''

const audits = await parallel(
  [
    { key: 'fanout', agent: 'fanout-maintainer' },
    { key: 'security', agent: 'security-maintainer' },
  ].map((a) => () =>
    agent(
      `Operate in REPORT mode (read-only). Audit the new route for your single dimension.${scopeNote}\n\nEndpoint: ${ask}`,
      { agentType: a.agent, label: `verify:${a.key}`, phase: 'Verify', schema: FINDINGS_SCHEMA }
    ).then((r) => ({ key: a.key, findings: (r && r.findings) || [] }))
  )
)

const gaps = audits
  .filter(Boolean)
  .flatMap((r) => r.findings.map((f) => ({ dimension: r.key, ...f })))
  .filter((f) => f.severity !== 'info')

let gapFix = 'no actionable gaps'
if (gaps.length) {
  gapFix = await agent(
    `Close these gaps on the new route with minimal root-cause fixes in the repo, then re-run the per-language checks. Do not run the suite. Do not commit.\n\nGaps:\n${JSON.stringify(gaps, null, 2)}\n\n${RULES}`,
    { label: 'fix-gaps', phase: 'Fix' }
  )
}

const test = await agent(
  `Operate in FIX mode. Write the integration test for this new route in the matching tier (api for a JSON/HTML route, e2e for an interactive UI flow) following the required patterns and the directory-mirrors-path layout. The route is not complete until it has a test. Create any missing package directories the test path needs. Validate by a clean import only. NEVER run the suite.\n\n${TESTS}\n\nEndpoint: ${ask}\nFiles changed:\n${changed.join('\n')}\n\nReturn the test file written and its tier.`,
  { agentType: 'test-maintainer', label: 'test', phase: 'Test' }
)

return { ask, map, build, audit: gaps, gapFix, test }
