#!/usr/bin/env node
/**
 * cli:scaffold-login-page — Scaffold an editable login page OVERRIDE into a
 * generated SmartStack client app, the UI counterpart to configure-login.
 *
 * Writes an editable React component the developer OWNS, and wires it through the
 * package's public override seam:
 *   - auth/{ComponentName}.tsx          → the login page (uses useLoginForm + PageProps)
 *   - extensions/login.generated.ts     → loginExtensions = { pages: { [PAGE_KEYS.LOGIN]: ... } }
 *   - main.tsx                          → import the wiring + spread loginExtensions into the provider config
 *
 * Works as soon as @atlashub/smartstack >= 3.55.0 is installed (the seam ships in
 * the package). It renders only the providers + registration link the spec asks
 * for — the UI-side masking the backend cannot do.
 */
import { parseArgs } from 'node:util';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { validate } from './validate.js';
import { generate, injectLoginExtensions, snippetForManualWiring } from './generate.js';
import { generateEnvelope, failGenerate, printEnvelope } from '../../../lib/output.js';

const COMMAND = 'scaffold-login-page';

async function main(): Promise<void> {
  const { values } = parseArgs({
    options: {
      spec: { type: 'string' },
      dry_run: { type: 'boolean', default: false },
    },
    strict: true,
  });

  if (!values.spec) {
    printEnvelope(failGenerate(COMMAND, ['--spec is required']));
    process.exit(1);
  }

  let raw: unknown;
  try {
    raw = JSON.parse(values.spec);
  } catch {
    printEnvelope(failGenerate(COMMAND, ['Invalid JSON in --spec']));
    process.exit(1);
  }

  const v = await validate(raw);
  if (!v.valid || !v.spec || !v.layout) {
    printEnvelope(failGenerate(COMMAND, v.errors));
    process.exit(1);
  }

  const spec = v.spec;
  const layout = v.layout;
  const projectRoot = resolve(spec.projectPath);
  const files = generate(spec, layout);

  const providersShown = Object.entries(spec.providers)
    .filter(([, on]) => on)
    .map(([k]) => k);

  const warnings = [...v.warnings];
  if (v.versionWarning) warnings.push(v.versionWarning);
  const nextSteps: string[] = [];

  // Honesty: the registration link is a UI affordance only.
  if (spec.allowRegistration) {
    warnings.push(
      'The "create account" link is shown, but the backend cannot disable signup either way — /api/auth/register stays open (license-quota limited).',
    );
  }
  if (spec.providers.entra) {
    nextSteps.push(
      'Entra SSO button → /sso requires VITE_MSAL_CLIENT_ID at build time (set it in the web app .env; see configure-login writeEnvExample).',
    );
  }

  if (values.dry_run) {
    printEnvelope(
      generateEnvelope(COMMAND, {
        data: {
          dryRun: true,
          files: files.map((f) => `${f.path} [${f.strategy}]`),
          componentName: spec.componentName,
          providersShown,
          allowRegistration: spec.allowRegistration,
          webDir: layout.webDir,
          smartStackVersion: v.smartStackVersion,
        },
        warnings,
      }),
    );
    process.exit(0);
  }

  const filesCreated: string[] = [];
  const filesModified: string[] = [];

  for (const file of files) {
    const abs = resolve(join(projectRoot, file.path));
    const exists = existsSync(abs);

    if (file.strategy === 'skip-if-exists') {
      if (exists && !spec.force) continue; // dev-owned — preserve edits
      if (exists && readFileSync(abs, 'utf-8') === file.content) continue;
      mkdirSync(dirname(abs), { recursive: true });
      writeFileSync(abs, file.content, 'utf-8');
      (exists ? filesModified : filesCreated).push(abs);
    } else {
      // overwrite (generated wiring)
      if (exists && !spec.force && readFileSync(abs, 'utf-8') === file.content) continue;
      mkdirSync(dirname(abs), { recursive: true });
      writeFileSync(abs, file.content, 'utf-8');
      (exists ? filesModified : filesCreated).push(abs);
    }
  }

  // Wire main.tsx (import the generated module + spread loginExtensions).
  const mainAbs = resolve(join(projectRoot, layout.webDir, 'src', 'main.tsx'));
  let providerWired = false;
  let providerSkipped = false;
  if (!existsSync(mainAbs)) {
    providerSkipped = true;
    warnings.push(`main.tsx not found at ${layout.webDir}/src/main.tsx — wiring skipped.`);
    nextSteps.push(snippetForManualWiring());
  } else {
    const src = readFileSync(mainAbs, 'utf-8');
    const { content, status } = injectLoginExtensions(src);
    if (status === 'applied') {
      writeFileSync(mainAbs, content, 'utf-8');
      filesModified.push(mainAbs);
      providerWired = true;
    } else if (status === 'unchanged') {
      providerWired = true;
    } else if (status === 'skipped-customised') {
      providerSkipped = true;
      warnings.push('main.tsx is marked `// @customised` — wiring skipped.');
      nextSteps.push(snippetForManualWiring());
    } else {
      providerSkipped = true;
      warnings.push('Could not find an `extensions: {…}` block in main.tsx — wiring skipped (was it customized?).');
      nextSteps.push(snippetForManualWiring());
    }
  }

  if (!v.smartStackVersion) {
    nextSteps.push(
      'Could not read the installed @atlashub/smartstack version — run `npm install` in the web app and ensure it is >= 3.55.0 (login-override seam).',
    );
  }
  nextSteps.push(
    `Edit web/${layout.webDir.split('/').pop()}/src/auth/${spec.componentName}.tsx to taste, then restart \`ss dev\` (or Vite) and open /login.`,
  );
  if (filesCreated.length === 0 && filesModified.length === 0) {
    nextSteps.unshift('Login page already up-to-date — no changes needed.');
  }

  printEnvelope(
    generateEnvelope(COMMAND, {
      data: {
        componentName: spec.componentName,
        providersShown,
        allowRegistration: spec.allowRegistration,
        webDir: layout.webDir,
        smartStackVersion: v.smartStackVersion,
        providerWired,
        providerSkipped,
        fileCount: filesCreated.length + filesModified.length,
      },
      filesCreated,
      filesModified,
      warnings,
      nextSteps,
    }),
  );
}

void main();
