/**
 * configure-login/validate.ts — Zod parse + filesystem resolution.
 *
 * Beyond schema validation this resolves the project layout (where appsettings
 * lives, where the web app lives) via the shared detector, and enforces the
 * secrets-safety rule: in 'local-file' mode, if .gitignore does NOT exclude
 * appsettings.Local.json, downgrade to 'placeholders' (never risk committing a
 * secret) and warn loudly.
 */
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 { ConfigureLoginInputSchema, type ConfigureLoginInput, type ProjectLayout } from './types.js';

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

function gitignoreExcludesLocal(projectRoot: string): boolean {
  const gi = path.join(projectRoot, '.gitignore');
  if (!existsSync(gi)) return false;
  try {
    const text = readFileSync(gi, 'utf-8');
    // Matches `appsettings.Local.json`, `appsettings.*.json`, `**/appsettings.Local.json`, etc.
    return /appsettings\.(\*|local)/i.test(text);
  } catch {
    return false;
  }
}

export async function validate(raw: unknown): Promise<ValidationResult> {
  const parsed = ConfigureLoginInputSchema.safeParse(raw);
  if (!parsed.success) {
    return {
      valid: false,
      spec: null,
      layout: null,
      errors: parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`),
      warnings: [],
    };
  }

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

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

  // Locate the .NET API folder (holds appsettings.json).
  const structure = await findSmartStackStructure(root);
  let apiAbs = structure.api ?? null;
  if (!apiAbs) {
    const hits = await findFiles('**/appsettings.json', { cwd: root });
    if (hits.length > 0) apiAbs = path.dirname(hits[0]);
  }
  if (!apiAbs) {
    errors.push(
      'Could not locate the .NET API folder (no appsettings.json found). Run against a generated SmartStack project.',
    );
    return { valid: false, spec, layout: null, errors, warnings };
  }

  // Locate the web app folder.
  let webAbs = structure.web ?? null;
  if (!webAbs) {
    webAbs = path.join(root, 'web', `${spec.appCode}-web`);
    warnings.push(`Web folder not auto-detected; assuming web/${spec.appCode}-web.`);
  }

  // Secrets safety: never risk committing a secret.
  if (spec.secretsMode === 'local-file' && !gitignoreExcludesLocal(root)) {
    spec.secretsMode = 'placeholders';
    warnings.push(
      '.gitignore does not exclude appsettings.Local.json — downgraded to placeholders mode (no secret written). ' +
        'Add `appsettings.Local.json` (or `appsettings.*.json`) to .gitignore, then re-run with secretsMode:"local-file".',
    );
  }

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

  return { valid: errors.length === 0, spec, layout, errors, warnings };
}
