/**
 * scaffold-login-page/generate.ts — PURE spec -> GeneratedFile[] (zero I/O).
 *
 * Emits, into web/{appCode}-web/src/:
 *   - auth/{ComponentName}.tsx          the editable login override (dev-owned)
 *   - extensions/login.generated.ts     wiring: loginExtensions = { pages: { [PAGE_KEYS.LOGIN]: ... } }
 *
 * and (via index.ts) wires main.tsx to import + spread `loginExtensions` into the
 * provider's `extensions: {}` — coexisting with any `...vitrineExtensions` spread
 * already there (the two register different PAGE_KEYS).
 *
 * The component consumes the package's public seam: `useLoginForm()` drives the
 * real /api/auth/login flow (tokens land in httpOnly cookies, redirects + onboarding
 * handled inside the hook — the component only owns presentation), OAuth buttons
 * hit /api/auth/{microsoft,google}, and the Entra SSO button goes to /sso. Only the
 * providers + registration link the spec asks for are emitted — that is the UI-side
 * masking the backend cannot do.
 */
import type { GeneratedFile, ProjectLayout, ScaffoldLoginPageInput } from './types.js';

const BANNER =
  '/* Scaffolded by /login-config (scaffold-login-page) — editable. Re-running keeps your edits (use force to overwrite). */\n';

/** JSON string literal for embedding in generated source. */
const s = (v: string): string => JSON.stringify(v);

function providerButton(label: string, target: string): string {
  return `            <button
              type="button"
              onClick={() => { globalThis.location.href = ${s(target)}; }}
              className="inline-flex w-full items-center justify-center gap-2 rounded-lg border border-[var(--border-color)] bg-[var(--bg-secondary)] px-4 py-2.5 font-medium transition-colors hover:bg-[var(--bg-tertiary)]"
            >
              ${label}
            </button>`;
}

// ============================================================================
// Login page component (dev-owned, editable; emitted once unless --force)
// ============================================================================
export function buildLoginComponent(spec: ScaffoldLoginPageInput): string {
  const { providers, branding } = spec;
  const comp = spec.componentName;
  const title = branding?.title ?? branding?.appName ?? 'Connexion';

  const imports = [
    `import type { ReactElement } from 'react';`,
    `import type { PageProps } from '@atlashub/smartstack';`,
  ];
  if (providers.local) imports.push(`import { useLoginForm } from '@atlashub/smartstack';`);

  // Branding header (text passed as JSX expressions so any character is safe).
  const brandingParts: string[] = [];
  if (branding?.logoUrl) {
    brandingParts.push(`          <img src={${s(branding.logoUrl)}} alt={${s(title)}} className="mx-auto mb-4 h-12 w-auto" />`);
  }
  brandingParts.push(`          <h1 className="text-2xl font-bold">{${s(title)}}</h1>`);
  if (branding?.subtitle) {
    brandingParts.push(`          <p className="mt-2 text-[var(--text-secondary)]">{${s(branding.subtitle)}}</p>`);
  }

  // Email/password form (only when the local provider is shown).
  let formBlock = '';
  if (providers.local) {
    const forgot = spec.showForgotPassword
      ? `
            <div className="text-right">
              <a href={${s(spec.forgotPasswordHref)}} className="text-sm text-primary-500 hover:underline">Mot de passe oublié&nbsp;?</a>
            </div>`
      : '';
    formBlock = `
          {error ? (
            <div className="mb-4 rounded-lg border border-[var(--border-color)] bg-[var(--bg-secondary)] px-3 py-2 text-sm text-[var(--color-danger,#dc2626)]">
              {error.message}
            </div>
          ) : null}

          <form onSubmit={handleSubmit} className="space-y-4">
            <div>
              <label htmlFor="email" className="mb-1 block text-sm font-medium">Email</label>
              <input
                id="email"
                type="email"
                autoComplete="email"
                required
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                className="w-full rounded-lg border border-[var(--border-color)] bg-[var(--bg-secondary)] px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500"
              />
            </div>
            <div>
              <label htmlFor="password" className="mb-1 block text-sm font-medium">Mot de passe</label>
              <div className="relative">
                <input
                  id="password"
                  type={showPassword ? 'text' : 'password'}
                  autoComplete="current-password"
                  required
                  value={password}
                  onChange={(e) => setPassword(e.target.value)}
                  className="w-full rounded-lg border border-[var(--border-color)] bg-[var(--bg-secondary)] px-3 py-2 pr-16 focus:outline-none focus:ring-2 focus:ring-primary-500"
                />
                <button
                  type="button"
                  onClick={() => setShowPassword((v) => !v)}
                  className="absolute right-2 top-1/2 -translate-y-1/2 text-sm text-[var(--text-tertiary)] hover:text-[var(--text-secondary)]"
                >
                  {showPassword ? 'Masquer' : 'Afficher'}
                </button>
              </div>
            </div>${forgot}
            <button
              type="submit"
              disabled={isLoading}
              className="inline-flex w-full items-center justify-center rounded-lg bg-primary-600 px-4 py-2.5 font-semibold text-white transition-colors hover:bg-primary-700 disabled:opacity-50"
            >
              {isLoading ? 'Connexion…' : 'Se connecter'}
            </button>
          </form>`;
  }

  // External providers (OAuth redirect / Entra SSO).
  const extButtons: string[] = [];
  if (providers.microsoft) extButtons.push(providerButton('Continuer avec Microsoft', '/api/auth/microsoft'));
  if (providers.google) extButtons.push(providerButton('Continuer avec Google', '/api/auth/google'));
  if (providers.entra) extButtons.push(providerButton('Connexion SSO (Entra ID)', '/sso'));

  let externalBlock = '';
  if (extButtons.length > 0) {
    const divider = providers.local
      ? `
          <div className="my-6 flex items-center gap-3">
            <div className="h-px flex-1 bg-[var(--border-color)]" />
            <span className="text-xs uppercase tracking-wider text-[var(--text-tertiary)]">ou</span>
            <div className="h-px flex-1 bg-[var(--border-color)]" />
          </div>`
      : '';
    externalBlock = `${divider}
          <div className="space-y-3">
${extButtons.join('\n')}
          </div>`;
  }

  // Registration link (UI affordance only — backend signup stays open regardless).
  const registerBlock = spec.allowRegistration
    ? `
          <p className="mt-6 text-center text-sm text-[var(--text-secondary)]">
            Pas encore de compte&nbsp;?{' '}
            <a href={${s(spec.registerHref)}} className="font-medium text-primary-500 hover:underline">Créer un compte</a>
          </p>`
    : '';

  const hookLine = providers.local
    ? `  const { email, setEmail, password, setPassword, showPassword, setShowPassword, isLoading, error, handleSubmit } = useLoginForm();\n\n`
    : '';

  return `${BANNER}${imports.join('\n')}

export function ${comp}(_props?: PageProps): ReactElement {
${hookLine}  return (
    <div className="flex min-h-screen items-center justify-center bg-[var(--bg-primary)] px-4">
      <div className="w-full max-w-md">
        <div className="mb-8 text-center">
${brandingParts.join('\n')}
        </div>
        <div className="rounded-2xl border border-[var(--border-color)] bg-[var(--bg-card)] p-8 shadow-lg">${formBlock}${externalBlock}${registerBlock}
        </div>
      </div>
    </div>
  );
}
`;
}

// ============================================================================
// Wiring module (regenerated each run)
// ============================================================================
export function buildWiringFile(spec: ScaffoldLoginPageInput): string {
  const comp = spec.componentName;
  return `/* AUTO-GENERATED by /login-config (scaffold-login-page) — regenerated on each run. Do not edit. */
import { PAGE_KEYS } from '@atlashub/smartstack';
import type { ExtensionConfig } from '@atlashub/smartstack';
import { ${comp} } from '../auth/${comp}';

export const loginExtensions: ExtensionConfig = { pages: { [PAGE_KEYS.LOGIN]: ${comp} } };
`;
}

// ============================================================================
// main.tsx wiring (idempotent string transform — coexists with vitrine)
// ============================================================================
export const LOGIN_IMPORT_LINE = `import { loginExtensions } from './extensions/login.generated';`;

export interface InjectResult {
  content: string;
  status: 'applied' | 'unchanged' | 'not-found' | 'skipped-customised';
}

/**
 * Ensure main.tsx (a) imports the wiring module and (b) spreads `loginExtensions`
 * into the provider's `extensions: {…}`. Handles an empty `extensions: {}` AND a
 * non-empty one (e.g. already carrying `...vitrineExtensions`) — the spread is
 * inserted right after the opening brace so both coexist. Idempotent; atomic
 * (bails to 'not-found' without writing a dangling import if no extensions block
 * is present).
 */
export function injectLoginExtensions(source: string): InjectResult {
  if (/\/\/\s*@customised/.test(source)) {
    return { content: source, status: 'skipped-customised' };
  }

  const hasImport = source.includes(LOGIN_IMPORT_LINE);
  const hasSpread = source.includes('...loginExtensions');
  if (hasImport && hasSpread) return { content: source, status: 'unchanged' };

  let content = source;

  if (!hasSpread) {
    if (/extensions:\s*\{\s*\}/.test(content)) {
      content = content.replace(/extensions:\s*\{\s*\}/, 'extensions: { ...loginExtensions }');
    } else if (/extensions:\s*\{/.test(content)) {
      content = content.replace(/extensions:\s*\{/, 'extensions: { ...loginExtensions,');
    } else {
      // No extensions block to spread into — don't leave a dangling import.
      return { content: source, status: 'not-found' };
    }
  }

  if (!hasImport) {
    content = `${LOGIN_IMPORT_LINE}\n${content}`;
  }

  return { content, status: 'applied' };
}

export function snippetForManualWiring(): string {
  return (
    'Wire the login override manually in web/<app>-web/src/main.tsx: add ' +
    `\`${LOGIN_IMPORT_LINE}\` near the other imports, and change ` +
    '`extensions: {}` to `extensions: { ...loginExtensions }` (or add `...loginExtensions` to the existing spread) in the SmartStackProvider config.'
  );
}

// ============================================================================
// Top-level generate
// ============================================================================
export function generate(spec: ScaffoldLoginPageInput, layout: ProjectLayout): GeneratedFile[] {
  const srcPrefix = `${layout.webDir}/src`;
  const comp = spec.componentName;
  return [
    {
      path: `${srcPrefix}/auth/${comp}.tsx`,
      content: buildLoginComponent(spec),
      strategy: 'skip-if-exists',
    },
    {
      path: `${srcPrefix}/extensions/login.generated.ts`,
      content: buildWiringFile(spec),
      strategy: 'overwrite',
    },
  ];
}
