/**
 * scaffold-vitrine/validate.ts — Zod parse + filesystem resolution + guards.
 *
 * Resolves the web app folder (where src/main.tsx lives), enforces the SDK's
 * reserved-path list for public routes, and performs a NON-BLOCKING version
 * guard: the public-route seam (PublicRouteRegistry / PAGE_KEYS.HOME) is present
 * in @atlashub/smartstack >= 3.55.0; below that we warn (never refuse) — the
 * generated TS build will surface a clear "not exported" error if the installed
 * package is too old.
 */
import path from 'node:path';
import { existsSync, statSync, readFileSync } from 'node:fs';
import { findSmartStackStructure } from '../../../lib/detector.js';
import { findFiles } from '../../../lib/fs.js';
import {
  ScaffoldVitrineInputSchema,
  isReservedPath,
  type ScaffoldVitrineInput,
  type ProjectLayout,
} from './types.js';

/** Floor where the public-route seam is verified present (@atlashub/smartstack). */
export const MIN_SMARTSTACK_VERSION = '3.55.0';

export interface ValidationResult {
  valid: boolean;
  spec: ScaffoldVitrineInput | null;
  layout: ProjectLayout | null;
  smartStackVersion: string | null;
  versionWarning: string | null;
  errors: string[];
  warnings: string[];
}

function parseSemver(v: string): [number, number, number] {
  const core = v.split('-')[0].split('+')[0];
  const parts = core.split('.').map((n) => parseInt(n, 10) || 0);
  return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
}

/** -1 if a<b, 0 if equal, 1 if a>b (major.minor.patch). */
export function cmpSemver(a: string, b: string): number {
  const pa = parseSemver(a);
  const pb = parseSemver(b);
  for (let i = 0; i < 3; i++) {
    if (pa[i] !== pb[i]) return pa[i] < pb[i] ? -1 : 1;
  }
  return 0;
}

function fail(extra: Partial<ValidationResult>): ValidationResult {
  return {
    valid: false,
    spec: null,
    layout: null,
    smartStackVersion: null,
    versionWarning: null,
    errors: [],
    warnings: [],
    ...extra,
  };
}

export async function validate(raw: unknown): Promise<ValidationResult> {
  const parsed = ScaffoldVitrineInputSchema.safeParse(raw);
  if (!parsed.success) {
    return fail({ errors: parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`) });
  }

  const spec = parsed.data;
  const errors: string[] = [];
  const warnings: string[] = [];

  const root = path.resolve(spec.projectPath);
  if (!existsSync(root)) return fail({ spec, errors: [`projectPath does not exist: ${root}`] });
  if (!statSync(root).isDirectory()) return fail({ spec, errors: [`projectPath is not a directory: ${root}`] });

  // Reserved-path enforcement (mirrors PublicRouteRegistry).
  for (const page of spec.pages ?? []) {
    if (isReservedPath(page.path)) {
      errors.push(`pages: path "${page.path}" is reserved by the SmartStack SDK and cannot be registered.`);
    }
  }
  if (errors.length > 0) return fail({ spec, errors });

  // Locate the web app folder (holds src/main.tsx).
  const structure = await findSmartStackStructure(root);
  let webAbs = structure.web ?? null;
  if (!webAbs) {
    const guess = path.join(root, 'web', `${spec.appCode}-web`);
    if (existsSync(guess)) {
      webAbs = guess;
      warnings.push(`Web folder not auto-detected; using web/${spec.appCode}-web.`);
    }
  }
  if (!webAbs) {
    const hits = await findFiles('**/src/main.tsx', { cwd: root });
    if (hits.length > 0) webAbs = path.dirname(path.dirname(hits[0]));
  }
  if (!webAbs) {
    return fail({
      spec,
      errors: ['Could not locate the web app (no src/main.tsx found). Run against a generated SmartStack project.'],
    });
  }

  // Non-blocking version guard.
  let smartStackVersion: string | null = null;
  let versionWarning: string | null = null;
  const pkgPath = path.join(webAbs, 'node_modules', '@atlashub', 'smartstack', 'package.json');
  if (existsSync(pkgPath)) {
    try {
      const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version?: unknown };
      if (typeof pkg.version === 'string') {
        smartStackVersion = pkg.version;
        if (cmpSemver(pkg.version, MIN_SMARTSTACK_VERSION) < 0) {
          versionWarning =
            `Installed @atlashub/smartstack ${pkg.version} is below ${MIN_SMARTSTACK_VERSION}; the public-route seam ` +
            `(PublicRouteRegistry / PAGE_KEYS) may be absent — upgrade the package or the generated imports will fail the TS build.`;
        }
      }
    } catch {
      /* malformed package.json — leave version unknown */
    }
  }

  const toRel = (abs: string): string => path.relative(root, abs).replace(/\\/g, '/');
  const layout: ProjectLayout = { webDir: toRel(webAbs) };

  return { valid: true, spec, layout, smartStackVersion, versionWarning, errors, warnings };
}
