/**
 * scaffold-vitrine/types.ts — Input contract + emitted file shape.
 *
 * This CLI SCAFFOLDS a public (pre-auth) marketing surface into a generated
 * SmartStack client app: the home (`/` override), presentation pages and a full
 * "site vitrine". Unlike login-config (which only writes CONFIG because the login
 * UI is package-owned with no seam), here the package ALREADY exposes the seam
 * (`PublicRouteRegistry`, `extensions.pages[PAGE_KEYS.HOME]`), so we emit REAL,
 * editable React pages the developer owns, and wire them via that seam.
 *
 * The copy lives in i18n (namespace `vitrine`); the layout/icons/hrefs are
 * structural and live in the generated components. Theme is delegated to
 * /scaffold-theme — the vitrine renders inside the SDK public shell and inherits
 * the SDK theme.css tokens.
 */
import { z } from 'zod';

// ── Shared ──────────────────────────────────────────────────────────────────
const CtaSchema = z.object({
  label: z.string().min(1),
  /** Absolute route ('/register') or anchor ('#features') or URL. Structural. */
  href: z.string().min(1),
});

// ── Section variants (discriminated by `type`) ───────────────────────────────
const HeroSectionSchema = z.object({
  type: z.literal('hero'),
  badge: z.string().min(1).optional(),
  headline: z.string().min(1),
  /** Gradient-highlighted tail of the title (rendered under the headline). */
  highlight: z.string().min(1).optional(),
  subhead: z.string().min(1).optional(),
  primaryCta: CtaSchema.optional(),
  secondaryCta: CtaSchema.optional(),
  /** Short "benefits" pills under the CTAs. */
  bullets: z.array(z.string().min(1)).optional(),
});

const FeatureItemSchema = z.object({
  /** lucide-react icon name (e.g. 'Users'); normalized to a safe allowlist. */
  icon: z.string().min(1).optional(),
  title: z.string().min(1),
  description: z.string().min(1),
});

const FeaturesSectionSchema = z.object({
  type: z.literal('features'),
  title: z.string().min(1).optional(),
  subtitle: z.string().min(1).optional(),
  items: z.array(FeatureItemSchema).min(1),
});

const SplitSectionSchema = z.object({
  type: z.literal('split'),
  title: z.string().min(1),
  highlight: z.string().min(1).optional(),
  body: z.string().min(1).optional(),
  bullets: z.array(z.string().min(1)).optional(),
  /** Which side the decorative visual sits on. */
  side: z.enum(['left', 'right']).default('left'),
  cta: CtaSchema.optional(),
});

const CtaSectionSchema = z.object({
  type: z.literal('cta'),
  headline: z.string().min(1),
  subhead: z.string().min(1).optional(),
  primaryCta: CtaSchema,
  secondaryCta: CtaSchema.optional(),
});

const FaqItemSchema = z.object({
  question: z.string().min(1),
  answer: z.string().min(1),
});

const FaqSectionSchema = z.object({
  type: z.literal('faq'),
  title: z.string().min(1).optional(),
  items: z.array(FaqItemSchema).min(1),
});

const LogosSectionSchema = z.object({
  type: z.literal('logos'),
  label: z.string().min(1).optional(),
  items: z.array(z.string().min(1)).min(1),
});

const SectionSchema = z.discriminatedUnion('type', [
  HeroSectionSchema,
  FeaturesSectionSchema,
  SplitSectionSchema,
  CtaSectionSchema,
  FaqSectionSchema,
  LogosSectionSchema,
]);

// ── Pages / home / branding ──────────────────────────────────────────────────
const PASCAL_CASE = /^[A-Z][A-Za-z0-9]*$/;
const ABSOLUTE_PATH = /^\/[A-Za-z0-9\-_/]*$/;

const HomeSchema = z.object({
  /** Presence already means "override `/`"; defaults true. */
  enabled: z.boolean().default(true),
  sections: z.array(SectionSchema).min(1),
});

const PageSchema = z.object({
  /** PascalCase → emitted as `{name}Page.tsx` + the registered component. */
  name: z.string().regex(PASCAL_CASE, 'name must be PascalCase (e.g. About, Pricing)'),
  /** Absolute route, must NOT be reserved by the SDK (validated in validate.ts). */
  path: z.string().regex(ABSOLUTE_PATH, 'path must be an absolute route like /about'),
  /**
   * 'public' → mounted under the SDK PublicLayout (Header + Footer).
   * 'none'   → white-label, no SDK chrome; uses the generated MarketingHeader/Footer.
   */
  layout: z.enum(['public', 'none']).default('public'),
  /** Optional human title (white-label header brand / document title). */
  title: z.string().min(1).optional(),
  sections: z.array(SectionSchema).min(1),
});

const BrandingSchema = z.object({
  appName: z.string().min(1).optional(),
  tagline: z.string().min(1).optional(),
  logoUrl: z.string().min(1).optional(),
});

export const ScaffoldVitrineInputSchema = z
  .object({
    projectPath: z.string().min(1),
    appCode: z.string().min(1),
    home: HomeSchema.optional(),
    pages: z.array(PageSchema).optional(),
    branding: BrandingSchema.optional(),
    languages: z.array(z.enum(['fr', 'en'])).min(1).default(['fr', 'en']),
    force: z.boolean().default(false),
  })
  .refine((s) => !!s.home || (s.pages?.length ?? 0) > 0, {
    message: 'Provide at least a home or one page.',
  })
  .refine(
    (s) => {
      const names = (s.pages ?? []).map((p) => p.name);
      return new Set(names).size === names.length;
    },
    { message: 'page names must be unique' },
  )
  .refine(
    (s) => {
      const paths = (s.pages ?? []).map((p) => p.path);
      return new Set(paths).size === paths.length;
    },
    { message: 'page paths must be unique' },
  );

export type ScaffoldVitrineInput = z.infer<typeof ScaffoldVitrineInputSchema>;
export type Section = z.infer<typeof SectionSchema>;
export type PageSpec = z.infer<typeof PageSchema>;

/**
 * Exact paths + prefixes reserved by the SmartStack SDK — MIRRORS
 * `PublicRouteRegistry` (web/smartstack-web/src/extensions/PublicRouteRegistry.ts
 * L7-28). A client may not register a public route at any of these. `/` is here
 * too — the home is taken via `extensions.pages[PAGE_KEYS.HOME]`, not the registry.
 */
const RESERVED_EXACT_PATHS: readonly string[] = [
  '/',
  '/login',
  '/sso',
  '/register',
  '/confirm-email',
  '/forgot-password',
  '/reset-password',
  '/force-change-password',
  '/auth/callback',
  '/auth/onboarding',
];
const RESERVED_PREFIXES: readonly string[] = ['/docs', '/t'];

export function isReservedPath(p: string): boolean {
  if (RESERVED_EXACT_PATHS.includes(p)) return true;
  return RESERVED_PREFIXES.some((prefix) => p === prefix || p.startsWith(`${prefix}/`));
}

/** Filesystem layout resolved by validate() (relative to projectPath, POSIX slashes). */
export interface ProjectLayout {
  /** Frontend folder holding src/main.tsx (e.g. 'web/demo-web'). */
  webDir: string;
}

export interface GeneratedFile {
  /** Relative to projectPath, POSIX slashes. */
  path: string;
  content: string;
  /**
   * - 'skip-if-exists'  : dev-owned source (sections, pages) — write if absent, keep edits.
   * - 'deep-merge-json' : i18n locales — add missing keys, preserve edited copy.
   * - 'overwrite'       : generated wiring — regenerated each run.
   */
  strategy: 'skip-if-exists' | 'deep-merge-json' | 'overwrite';
}
