/**
 * scaffold-login-page/types.ts — Input contract + emitted file shape.
 *
 * This CLI SCAFFOLDS a real, editable login page OVERRIDE into a generated
 * SmartStack client app — the counterpart to configure-login (which only writes
 * backend/config). The package exposes a public seam for this:
 * `extensions.pages[PAGE_KEYS.LOGIN]` replaces the default LoginPage on `/login`
 * (DynamicRouter → withPageOverride), and `useLoginForm()` / `PageProps` are
 * exported by @atlashub/smartstack (>= 3.55.0). So we emit a component the
 * developer OWNS that wires the real auth flow (email/password via useLoginForm,
 * OAuth via /api/auth/{provider}, Entra SSO via /sso) and renders only the
 * providers + the registration link the operator asked for — the UI-side
 * masking the backend can't do (it has no GET /api/auth/config, and
 * /api/auth/register stays open regardless).
 *
 * Theme is delegated to /scaffold-theme: the page uses theme.css tokens
 * (--bg-card, --border-color, --text-secondary, primary-* utilities) and
 * inherits the app theme.
 */
import { z } from 'zod';

const PASCAL_CASE = /^[A-Z][A-Za-z0-9]*$/;

/** Which sign-in affordances to RENDER on the page (UI only — distinct from the
 *  backend connectionTypes that configure-login writes). At least one required. */
const ProvidersSchema = z
  .object({
    /** Email/password form (wired through useLoginForm). */
    local: z.boolean().default(true),
    /** "Continue with Microsoft" → GET /api/auth/microsoft. */
    microsoft: z.boolean().default(false),
    /** "Continue with Google" → GET /api/auth/google. */
    google: z.boolean().default(false),
    /** Entra ID SSO button → /sso (needs VITE_MSAL_CLIENT_ID at build time). */
    entra: z.boolean().default(false),
  })
  .refine((p) => p.local || p.microsoft || p.google || p.entra, {
    message: 'At least one provider must be shown (local, microsoft, google, or entra).',
  });

const BrandingSchema = z.object({
  /** Shown as the heading (falls back to a generic title). */
  appName: z.string().min(1).optional(),
  /** Optional logo image URL rendered above the heading. */
  logoUrl: z.string().min(1).optional(),
  /** Override the form heading (defaults to appName or "Connexion"). */
  title: z.string().min(1).optional(),
  /** Optional sub-heading under the title. */
  subtitle: z.string().min(1).optional(),
});

export const ScaffoldLoginPageInputSchema = z.object({
  projectPath: z.string().min(1),
  appCode: z.string().min(1),
  /** Emitted as src/auth/{componentName}.tsx + registered for PAGE_KEYS.LOGIN. */
  componentName: z
    .string()
    .regex(PASCAL_CASE, 'componentName must be PascalCase (e.g. CustomLoginPage)')
    .default('CustomLoginPage'),
  providers: ProvidersSchema,
  /** Show the "create account" link. The backend cannot block signup, so this is
   *  purely the UI affordance (mirrors configure-login.allowRegistration). */
  allowRegistration: z.boolean().default(false),
  branding: BrandingSchema.optional(),
  /** Where the "create account" link points (the package serves /register). */
  registerHref: z.string().min(1).default('/register'),
  /** Where the "forgot password" link points (the package serves /forgot-password). */
  forgotPasswordHref: z.string().min(1).default('/forgot-password'),
  /** Render the "forgot password" link (only meaningful with the local form). */
  showForgotPassword: z.boolean().default(true),
  /** Overwrite the page component even if it already exists (loses dev edits). */
  force: z.boolean().default(false),
});

export type ScaffoldLoginPageInput = z.infer<typeof ScaffoldLoginPageInputSchema>;

/** 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 page component — write if absent, keep edits.
   * - 'overwrite'      : generated wiring module — regenerated each run.
   */
  strategy: 'skip-if-exists' | 'overwrite';
}
