#!/usr/bin/env node
/**
 * cli:scaffold-vitrine — Scaffold a public (pre-auth) marketing surface into a
 * generated SmartStack client app: the home (`/` override), presentation pages
 * and a full site vitrine.
 *
 * Writes editable React pages + sections + i18n the developer OWNS, and wires
 * them through the package's existing public seam:
 *   - extensions/vitrine.generated.ts  → PublicRouteRegistry.register + addClientResources + vitrineExtensions
 *   - main.tsx                         → import the wiring + spread vitrineExtensions into the provider config
 *
 * Unlike login-config there is NO package-contract dependency: the seam ships in
 * @atlashub/smartstack (>= 3.55.0), so this works as soon as that version is installed.
 */
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,
  injectVitrineExtensions,
  snippetForManualWiring,
  countSections,
} from './generate.js';
import { deepMerge } from '../../../lib/json-merge.js';
import { generateEnvelope, failGenerate, printEnvelope } from '../../../lib/output.js';

const COMMAND = 'scaffold-vitrine';

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 warnings = [...v.warnings];
  if (v.versionWarning) warnings.push(v.versionWarning);
  const nextSteps: string[] = [];

  const pagesRegistered = (spec.pages ?? []).map((p) => ({ path: p.path, layout: p.layout }));

  if (values.dry_run) {
    printEnvelope(
      generateEnvelope(COMMAND, {
        data: {
          dryRun: true,
          files: files.map((f) => `${f.path} [${f.strategy}]`),
          homeOverridden: !!spec.home?.enabled,
          pagesRegistered,
          sectionsGenerated: countSections(spec),
          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 === 'deep-merge-json') {
      let existing: unknown = {};
      if (exists) {
        try {
          existing = JSON.parse(readFileSync(abs, 'utf-8'));
        } catch {
          existing = {};
        }
      }
      const merged = deepMerge(existing, JSON.parse(file.content));
      const out = `${JSON.stringify(merged, null, 2)}\n`;
      if (exists && !spec.force && readFileSync(abs, 'utf-8') === out) continue;
      mkdirSync(dirname(abs), { recursive: true });
      writeFileSync(abs, out, 'utf-8');
      (exists ? filesModified : filesCreated).push(abs);
    } else 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 vitrineExtensions).
  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 } = injectVitrineExtensions(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 `extensions: {}` in main.tsx — wiring skipped (was it customized?).');
      nextSteps.push(snippetForManualWiring());
    }
  }

  // nextSteps.
  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 (public-route seam).',
    );
  }
  if (spec.languages.length > 1) {
    const others = spec.languages
      .slice(1)
      .map((l) => `${layout.webDir}/src/vitrine/locales/${l}/vitrine.json`)
      .join(', ');
    nextSteps.push(`Translate the non-primary locale file(s): ${others} (they were seeded with the same copy).`);
  }
  if ((spec.pages ?? []).some((p) => p.layout === 'none')) {
    nextSteps.push(
      'White-label pages (layout:"none") render without the SmartStack Header/Footer; they use the generated MarketingHeader/MarketingFooter — edit those to taste.',
    );
  }
  nextSteps.push('Restart `ss dev` (or the Vite dev server) to see the vitrine. The home is at `/`; presentation pages at their routes.');
  if (filesCreated.length === 0 && filesModified.length === 0) {
    nextSteps.unshift('Vitrine already up-to-date — no changes needed.');
  }

  printEnvelope(
    generateEnvelope(COMMAND, {
      data: {
        homeOverridden: !!spec.home?.enabled,
        pagesRegistered,
        sectionsGenerated: countSections(spec),
        webDir: layout.webDir,
        smartStackVersion: v.smartStackVersion,
        providerWired,
        providerSkipped,
        fileCount: filesCreated.length + filesModified.length,
      },
      filesCreated,
      filesModified,
      warnings,
      nextSteps,
    }),
  );
}

void main();
