#!/usr/bin/env node

/**
 * Frontend Builder Agent — SF-03
 *
 * 根据 PRD (SF-01) + Architecture (SF-02) 自动生成前端项目。
 *
 * 技术栈：Next.js 15 App Router + TypeScript + Tailwind CSS
 *
 * 生成内容：
 *   - 页面组件（非 stub，包含真实 JSX/state/hooks）
 *   - Root Layout + 导航
 *   - 共享组件（Card, Button, Input, Modal, 领域组件）
 *   - Custom Hooks（数据获取/表单/状态）
 *   - API Services（类型安全的 API 调用层）
 *   - TypeScript 类型定义
 *   - 项目配置（package.json, tsconfig, tailwind, next.config）
 *
 * Usage:
 *   node scripts/frontend-builder-agent.mjs --prd <path> --arch <path> [options]
 *   node scripts/frontend-builder-agent.mjs --input-text "<需求>" [options]
 *
 * Options:
 *   --prd <path>          PRD JSON（SF-01 输出）
 *   --arch <path>         Architecture JSON（SF-02 输出）
 *   --input-text <text>   直接传入需求（自动调 SF-01 → SF-02 → SF-03）
 *   --output <dir>        输出目录（default: frontend/）
 *   --verbose             详细输出
 *   --help                显示帮助
 *
 * @module frontend-builder-agent
 */

import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { createContract, toCamel, tsType as contractTsType, createInputFields } from "./model-contract.mjs";

const __dirname = dirname(resolve(fileURLToPath(import.meta.url)));
const WORKSPACE = resolve(__dirname, "..");
const DEFAULT_OUTPUT = resolve(WORKSPACE, "frontend");

// ═══════════════════════════════════════════════════════
// 1. TypeScript Type Generator
// ═══════════════════════════════════════════════════════

/**
 * Map domain to TypeScript interfaces.
 */
function generateTypes(prd, arch, contract) {
  const domain = prd.domain || "generic";

  const interfaces = [`// Auto-generated types for ${prd.projectName}`];
  interfaces.push("\n// ─── Base ───────────────────────────────────────────\n");

  // Base types
  interfaces.push(`
// ─── Base ───────────────────────────────────────────
export interface User {
  id: string;
  phone: string;
  nickname: string;
  avatarUrl: string;
  createdAt: string;
  updatedAt: string;
}`);

  // ─── Entity types from Contract ───
  for (const entity of contract.entities.filter(e => e.table !== "users")) {
    const name = entity.name;
    interfaces.push(`export interface ${name} {`);
    for (const f of entity.fields) {
      const fname = toCamel(contract, f.name);
      const ft = contractTsType(f);
      const optional = f.required ? "" : "?";
      interfaces.push(`  ${fname}${optional}: ${ft};`);
    }
    interfaces.push("}\n");
  }

  // Domain-specific extra types (legacy — for pages that need them)
  const domainTypes = {
    pet: `
export interface Pet {
  id: string;
  ownerId: string;
  name: string;
  species: string;
  breed: string;
  birthDate: string;
  weightKg: number;
  avatarUrl: string;
  createdAt: string;
}

export interface Schedule {
  id: string;
  petId: string;
  type: "vaccine" | "deworming" | "checkup" | "grooming";
  title: string;
  scheduledAt: string;
  completed: boolean;
}

export interface DailyLog {
  id: string;
  petId: string;
  category: "food" | "exercise" | "excretion" | "weight";
  value: string;
  loggedAt: string;
}

export interface Hospital {
  id: string;
  name: string;
  address: string;
  phone: string;
  rating: number;
  distance: number;
  services: string[];
}`,

    ecommerce: `
export interface Product {
  id: string;
  title: string;
  price: number;
  stock: number;
  images: string[];
  categoryId: string;
  createdAt: string;
}

export interface Order {
  id: string;
  userId: string;
  status: "pending" | "paid" | "shipped" | "delivered" | "cancelled";
  totalAmount: number;
  addressId: string;
  createdAt: string;
}

export interface Logistics {
  id: string;
  orderId: string;
  carrier: string;
  trackingNumber: string;
  status: string;
  events: LogisticsEvent[];
}

export interface LogisticsEvent {
  timestamp: string;
  location: string;
  description: string;
}`,

    education: `
export interface Course {
  id: string;
  title: string;
  instructorId: string;
  price: number;
  coverUrl: string;
  createdAt: string;
}

export interface Lesson {
  id: string;
  courseId: string;
  title: string;
  videoUrl: string;
  durationSec: number;
  sortOrder: number;
}

export interface Exercise {
  id: string;
  lessonId: string;
  question: string;
  options: string[];
  answer: string;
}

export interface Progress {
  id: string;
  userId: string;
  lessonId: string;
  completed: boolean;
  watchedSec: number;
}`,

    enterprise: `
export interface Department {
  id: string;
  name: string;
  parentId: string | null;
  managerId: string;
}

export interface Approval {
  id: string;
  applicantId: string;
  type: "leave" | "expense" | "overtime" | "travel";
  status: "pending" | "approved" | "rejected";
  formData: Record<string, unknown>;
  createdAt: string;
}

export interface Attendance {
  id: string;
  userId: string;
  checkInAt: string;
  checkOutAt: string | null;
  location: { lat: number; lng: number; address: string };
  type: "normal" | "remote" | "overtime";
}`,

    fitness: `
export interface CheckIn {
  id: string;
  userId: string;
  type: string;
  durationMin: number;
  calories: number;
  note: string;
  checkedAt: string;
}

export interface TrainingPlan {
  id: string;
  userId: string;
  title: string;
  schedule: Record<string, unknown>;
  active: boolean;
}`,

    note: `
export interface Note {
  id: string;
  userId: string;
  title: string;
  content: string;
  isMarkdown: boolean;
  folderId: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface Tag {
  id: string;
  name: string;
  color: string;
}`,
  };

  // (Entity types now generated from contract above)

  // API response types
  interfaces.push(`
// ─── API Responses ──────────────────────────────────
export interface ApiResponse<T> {
  data: T;
  message: string;
}

export interface PaginatedResponse<T> extends ApiResponse<T[]> {
  total: number;
  page: number;
  pageSize: number;
}

export interface ApiError {
  code: string;
  message: string;
  details?: Record<string, string[]>;
}`);

  return interfaces.join("\n");
}

// ═══════════════════════════════════════════════════════
// 2. API Service Generator
// ═══════════════════════════════════════════════════════

function generateApiServices(prd, arch) {
  const apiDesign = arch.apiDesign || [];
  const files = {};

  // Base API client
  files["services/api.ts"] = `// Auto-generated API client for ${prd.projectName}

const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001/api";

interface RequestOptions extends RequestInit {
  params?: Record<string, string | number>;
}

async function request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
  const { params, ...init } = options;
  let url = \`\${API_BASE}\${endpoint}\`;

  if (params) {
    const searchParams = new URLSearchParams();
    Object.entries(params).forEach(([k, v]) => searchParams.set(k, String(v)));
    url += \`?\${searchParams.toString()}\`;
  }

  const res = await fetch(url, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...init.headers,
    },
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({ message: res.statusText }));
    throw new Error(err.message || \`API Error: \${res.status}\`);
  }

  return res.json();
}

export const api = {
  get: <T>(url: string, params?: Record<string, string | number>) =>
    request<T>(url, { method: "GET", params }),

  post: <T>(url: string, data?: unknown) =>
    request<T>(url, { method: "POST", body: JSON.stringify(data) }),

  put: <T>(url: string, data?: unknown) =>
    request<T>(url, { method: "PUT", body: JSON.stringify(data) }),

  delete: <T>(url: string) =>
    request<T>(url, { method: "DELETE" }),
};
`;

  // Resource-specific services
  for (const group of apiDesign) {
    const resource = group.resource;
    const endpoints = group.endpoints;

    // Build service methods
    const methods = [];
    for (const ep of endpoints) {
      const fnName = ep.method.toLowerCase() + ep.path
        .replace(`/api/${resource}`, "")
        .replace(/[\/:]/g, "_")
        .replace(/^_/, "") || "list";

      let fnBody;
      let fnParams = "";

      if (ep.method === "GET") {
        if (ep.path.includes(":id")) {
          fnParams = "id: string";
          fnBody = `api.get(\`/api/${resource}/\${id}\`)`;
        } else {
          fnBody = `api.get("/api/${resource}")`;
        }
      } else if (ep.method === "POST") {
        fnBody = `api.post("/api/${resource}", data)`;
        fnParams = `data: Record<string, unknown>`;
      } else if (ep.method === "PUT") {
        fnParams = `id: string, data: Record<string, unknown>`;
        fnBody = `api.put(\`/api/${resource}/\${id}\`, data)`;
      } else if (ep.method === "DELETE") {
        fnParams = "id: string";
        fnBody = `api.delete(\`/api/${resource}/\${id}\`)`;
      }

      // Clean up function name
      const cleanName = fnName.replace(/_{2,}/g, "_").replace(/_$/, "") || "list";

      methods.push(`export function ${cleanName}(${fnParams}) {\n  return ${fnBody};\n}`);
    }

    files[`services/${resource}.ts`] = `// Auto-generated ${resource} service
import { api } from "./api";

${methods.join("\n\n")}
`;
  }

  return files;
}

function capitalize(s) {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

// ═══════════════════════════════════════════════════════
// 3. Custom Hooks Generator
// ═══════════════════════════════════════════════════════

function generateHooks(prd, arch) {
  const apiDesign = arch.apiDesign || [];
  const files = {};

  for (const group of apiDesign) {
    const resource = group.resource;
    // Sanitize: "daily-logs" → "DailyLogs", remove any non-alphanumeric except underscore
    const sanitized = capitalize(resource).replace(/[^a-zA-Z0-9_]/g, "");
    const name = sanitized || "Item";

    // Singularize type name: Albums → Album, Pets → Pet
    const singularName = name.replace(/ies$/, "y").replace(/ses$/, "s").replace(/xes$/, "x").replace(/ches$/, "ch").replace(/shes$/, "sh").replace(/s$/i, "");
    const typeNameForHook = singularName || name;

    files[`hooks/use${name}.ts`] = `"use client";

import { useState, useEffect, useCallback } from "react";

// Generic data type for ${resource}
type ${typeNameForHook} = Record<string, unknown>;

/**
 * Fetch and manage ${resource} data.
 */
export function use${name}() {
  const [data, setData] = useState<${typeNameForHook}[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const fetchData = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);
      const res = await fetch(\`/api/${resource}\`);
      const json = await res.json();
      setData(Array.isArray(json) ? json : json?.data || []);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Failed to load data");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  return { data, loading, error, refetch: fetchData };
}
`;
  }

  // Generic hooks
  files["hooks/useForm.ts"] = `"use client";

import { useState, useCallback } from "react";

interface UseFormOptions<T> {
  initialValues: T;
  onSubmit: (values: T) => Promise<void>;
}

export function useForm<T extends Record<string, unknown>>({ initialValues, onSubmit }: UseFormOptions<T>) {
  const [values, setValues] = useState<T>(initialValues);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const setField = useCallback(<K extends keyof T>(field: K, value: T[K]) => {
    setValues(prev => ({ ...prev, [field]: value }));
  }, []);

  const handleSubmit = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      setSubmitting(true);
      setError(null);
      await onSubmit(values);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Submit failed");
    } finally {
      setSubmitting(false);
    }
  }, [values, onSubmit]);

  return { values, setField, submitting, error, handleSubmit };
}
`;

  files["hooks/useDebounce.ts"] = `"use client";

import { useState, useEffect } from "react";

export function useDebounce<T>(value: T, delay: number = 300): T {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}
`;

  return files;
}

// ═══════════════════════════════════════════════════════
// 4. Component Generator
// ═══════════════════════════════════════════════════════

function generateComponents(prd, arch) {
  const files = {};
  const domain = prd.domain || "generic";
  const projectName = prd.projectName || "App";

  // ── Shared UI Components ──────────────────────

  files["components/ui/Button.tsx"] = `"use client";

import { type ButtonHTMLAttributes } from "react";

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: "primary" | "secondary" | "danger" | "ghost";
  size?: "sm" | "md" | "lg";
  loading?: boolean;
}

export function Button({ variant = "primary", size = "md", loading, children, className = "", disabled, ...props }: ButtonProps) {
  const base = "inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
  const variants = {
    primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
    secondary: "bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-400",
    danger: "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
    ghost: "text-gray-600 hover:bg-gray-100 focus:ring-gray-400",
  };
  const sizes = {
    sm: "px-3 py-1.5 text-sm",
    md: "px-4 py-2 text-sm",
    lg: "px-6 py-3 text-base",
  };

  return (
    <button className={\`\${base} \${variants[variant]} \${sizes[size]} \${className}\`} disabled={disabled || loading} {...props}>
      {loading && <svg className="animate-spin -ml-1 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" /><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" /></svg>}
      {children}
    </button>
  );
}
`;

  files["components/ui/Card.tsx"] = `import type { ReactNode } from "react";

interface CardProps {
  children: ReactNode;
  className?: string;
  onClick?: () => void;
}

export function Card({ children, className = "", onClick }: CardProps) {
  return (
    <div
      className={\`bg-white rounded-xl shadow-sm border border-gray-100 p-4 \${onClick ? "cursor-pointer hover:shadow-md transition-shadow" : ""} \${className}\`}
      onClick={onClick}
    >
      {children}
    </div>
  );
}
`;

  files["components/ui/Input.tsx"] = `"use client";

import { type InputHTMLAttributes } from "react";

interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  error?: string;
}

export function Input({ label, error, className = "", id, ...props }: InputProps) {
  return (
    <div className="w-full">
      {label && <label htmlFor={id} className="block text-sm font-medium text-gray-700 mb-1">{label}</label>}
      <input
        id={id}
        className={\`w-full px-3 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent \${error ? "border-red-300" : "border-gray-300"} \${className}\`}
        {...props}
      />
      {error && <p className="mt-1 text-xs text-red-500">{error}</p>}
    </div>
  );
}
`;

  files["components/ui/Modal.tsx"] = `"use client";

import { useEffect, type ReactNode } from "react";

interface ModalProps {
  open: boolean;
  onClose: () => void;
  title: string;
  children: ReactNode;
}

export function Modal({ open, onClose, title, children }: ModalProps) {
  useEffect(() => {
    if (open) document.body.style.overflow = "hidden";
    else document.body.style.overflow = "";
    return () => { document.body.style.overflow = ""; };
  }, [open]);

  if (!open) return null;

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      <div className="absolute inset-0 bg-black/40" onClick={onClose} />
      <div className="relative bg-white rounded-2xl shadow-xl max-w-lg w-full mx-4 p-6">
        <div className="flex items-center justify-between mb-4">
          <h2 className="text-lg font-semibold">{title}</h2>
          <button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-xl leading-none">&times;</button>
        </div>
        {children}
      </div>
    </div>
  );
}
`;

  files["components/ui/EmptyState.tsx"] = `import type { ReactNode } from "react";

interface EmptyStateProps {
  icon?: string;
  title: string;
  description?: string;
  action?: ReactNode;
}

export function EmptyState({ icon = "📭", title, description, action }: EmptyStateProps) {
  return (
    <div className="flex flex-col items-center justify-center py-16 text-center">
      <span className="text-5xl mb-4">{icon}</span>
      <h3 className="text-lg font-medium text-gray-700 mb-1">{title}</h3>
      {description && <p className="text-sm text-gray-500 mb-4">{description}</p>}
      {action}
    </div>
  );
}
`;

  // ── Domain-specific components ──────────────────

  if (domain === "pet") {
    files["components/pet/PetCard.tsx"] = `import type { Pet } from "@/types";
import { Card } from "@/components/ui/Card";

interface PetCardProps {
  pet: Pet;
  onClick?: () => void;
}

export function PetCard({ pet, onClick }: PetCardProps) {
  return (
    <Card onClick={onClick} className="flex items-center gap-4">
      <div className="w-16 h-16 rounded-full bg-gradient-to-br from-amber-100 to-orange-200 flex items-center justify-center text-2xl overflow-hidden flex-shrink-0">
        {pet.avatarUrl ? (
          <img src={pet.avatarUrl} alt={pet.name} className="w-full h-full object-cover" />
        ) : (
          "🐾"
        )}
      </div>
      <div className="flex-1 min-w-0">
        <h3 className="font-semibold text-gray-800 truncate">{pet.name}</h3>
        <p className="text-sm text-gray-500">{pet.breed || pet.species} · {pet.weightKg}kg</p>
      </div>
      <svg className="w-5 h-5 text-gray-300 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
      </svg>
    </Card>
  );
}
`;

    files["components/pet/ScheduleCard.tsx"] = `import type { Schedule } from "@/types";

interface ScheduleCardProps {
  schedule: Schedule;
  onToggle?: () => void;
}

export function ScheduleCard({ schedule, onToggle }: ScheduleCardProps) {
  const typeIcons: Record<string, string> = {
    vaccine: "💉",
    deworming: "💊",
    checkup: "🩺",
    grooming: "✂️",
  };

  const date = new Date(schedule.scheduledAt);
  const isOverdue = date < new Date() && !schedule.completed;

  return (
    <div className={\`flex items-center gap-3 p-3 rounded-lg border \${schedule.completed ? "bg-gray-50 border-gray-100" : isOverdue ? "bg-red-50 border-red-200" : "bg-white border-gray-100"}\`}>
      <span className="text-2xl">{typeIcons[schedule.type] || "📅"}</span>
      <div className="flex-1 min-w-0">
        <p className={\`text-sm font-medium \${schedule.completed ? "text-gray-400 line-through" : "text-gray-800"}\`}>{schedule.title}</p>
        <p className="text-xs text-gray-500">{date.toLocaleDateString("zh-CN", { month: "short", day: "numeric" })}</p>
      </div>
      <button
        onClick={onToggle}
        className={\`w-6 h-6 rounded-full border-2 flex items-center justify-center flex-shrink-0 \${schedule.completed ? "bg-green-500 border-green-500" : "border-gray-300"}\`}
      >
        {schedule.completed && <svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" /></svg>}
      </button>
    </div>
  );
}
`;
  }

  if (domain === "ecommerce") {
    files["components/ecommerce/ProductCard.tsx"] = `import type { Product } from "@/types";
import { Card } from "@/components/ui/Card";

interface ProductCardProps {
  product: Product;
  onClick?: () => void;
}

export function ProductCard({ product, onClick }: ProductCardProps) {
  return (
    <Card onClick={onClick} className="overflow-hidden p-0">
      <div className="aspect-square bg-gray-100 flex items-center justify-center text-4xl">
        {product.images?.[0] ? <img src={product.images[0]} alt={product.title} className="w-full h-full object-cover" /> : "📦"}
      </div>
      <div className="p-3">
        <h3 className="font-medium text-sm text-gray-800 truncate">{product.title}</h3>
        <div className="flex items-center justify-between mt-1">
          <span className="text-red-500 font-bold">¥{product.price}</span>
          <span className="text-xs text-gray-400">库存 {product.stock}</span>
        </div>
      </div>
    </Card>
  );
}
`;
  }

  if (domain === "note") {
    files["components/note/NoteCard.tsx"] = `import type { Note } from "@/types";
import { Card } from "@/components/ui/Card";

interface NoteCardProps {
  note: Note;
  onClick?: () => void;
}

export function NoteCard({ note, onClick }: NoteCardProps) {
  return (
    <Card onClick={onClick}>
      <h3 className="font-medium text-gray-800 truncate">{note.title || "无标题"}</h3>
      <p className="text-sm text-gray-500 mt-1 line-clamp-2">{note.content?.slice(0, 120) || "空笔记"}</p>
      <div className="flex items-center gap-2 mt-2">
        {note.isMarkdown && <span className="text-xs px-1.5 py-0.5 bg-purple-100 text-purple-600 rounded">MD</span>}
        <span className="text-xs text-gray-400">{new Date(note.updatedAt).toLocaleDateString("zh-CN")}</span>
      </div>
    </Card>
  );
}
`;
  }

  return files;
}

// ═══════════════════════════════════════════════════════
// 5. Page Generator
// ═══════════════════════════════════════════════════════

function generatePages(prd, arch) {
  const pages = prd.pages || [];
  const apiDesign = arch.apiDesign || [];
  const domain = prd.domain || "generic";
  const projectName = prd.projectName || "App";
  const files = {};

  // ── Home Page ──────────────────────────────────

  const homePage = pages.find(p => p.route === "/home" || p.name === "首页" || p.name === "主页");
  if (homePage || pages.length > 0) {
    const features = prd.features?.slice(0, 4).map(f => f.name) || [];
    files["app/page.tsx"] = generateHomePage(projectName, domain, homePage || pages[0], features);
  }

  // ── Dynamic pages ──────────────────────────────

  for (const page of pages) {
    const route = page.route;
    if (!route || route === "/home") continue;

    // Clean route: /pet/:id → pet/[id], /daily-log → daily-log
    const dirPath = route
      .replace(/^\//, "")
      .replace(/:(\w+)/g, "[$1]")
      .replace(/\/$/, "");

    const pageContent = generatePageComponent(page, domain, apiDesign, prd.features);
    files[`app/${dirPath}/page.tsx`] = pageContent;
  }

  return files;
}

/**
 * Generate a full, meaningful home page component.
 */
function generateHomePage(projectName, domain, homePage, features) {
  const name = homePage?.name || "首页";
  const desc = homePage?.description || `${projectName} 首页`;

  return `import Link from "next/link";

export default function HomePage() {
  return (
    <div className="space-y-6">
      {/* Hero Section */}
      <section className="bg-gradient-to-br from-blue-500 to-blue-700 rounded-2xl p-6 text-white">
        <h1 className="text-2xl font-bold">${projectName}</h1>
        <p className="mt-2 text-blue-100 text-sm">${desc}</p>
      </section>

      {/* Quick Actions */}
      <section>
        <h2 className="text-lg font-semibold text-gray-800 mb-3">快捷入口</h2>
        <div className="grid grid-cols-2 gap-3">
          ${features.map((f, i) => {
            const icons = ["📋", "📅", "📝", "📸", "🏥", "📚", "💬"];
            return `<Link key="${i}" href="/home" className="bg-white rounded-xl border border-gray-100 p-4 text-center hover:shadow-md transition-shadow">
            <span className="text-2xl block mb-1">${icons[i] || "📌"}</span>
            <span className="text-sm text-gray-600">${f}</span>
          </Link>`;
          }).join("\n          ")}
        </div>
      </section>

      {/* Recent Activity / Stats */}
      <section>
        <h2 className="text-lg font-semibold text-gray-800 mb-3">今日概览</h2>
        <div className="bg-white rounded-xl border border-gray-100 p-4">
          <div className="flex items-center justify-between">
            <div>
              <p className="text-sm text-gray-500">欢迎使用 ${projectName}</p>
              <p className="text-xs text-gray-400 mt-1">开始探索功能吧</p>
            </div>
            <span className="text-3xl">👋</span>
          </div>
        </div>
      </section>
    </div>
  );
}
`;
}

/**
 * Generate a real page component (not a stub).
 */
function generatePageComponent(page, domain, apiDesign, features) {
  const name = page.name;
  const route = page.route;
  const desc = page.description;

  // Resource name from route: /pet/:id → pets, /schedule → schedules
  const resourceSeg = route.replace(/^\//, "").split("/")[0].replace(/:.*/, "");

  // Find matching API group
  const apiGroup = apiDesign.find(g =>
    g.resource === resourceSeg ||
    g.resource === resourceSeg + "s" ||
    g.resource + "s" === resourceSeg
  );

  const hookNameRaw = apiGroup ? capitalize(apiGroup.resource) : null;
  // Singularize for type references: albums → Album, pets → Pet
  const rawTypeName = apiGroup ? capitalize(apiGroup.resource) : null;
  const typeName = rawTypeName ? rawTypeName.replace(/ies$/, "y").replace(/ses$/, "s").replace(/xes$/, "x").replace(/ches$/, "ch").replace(/shes$/, "sh").replace(/s$/i, "") : null;

  // Generate realistic page content
  switch (domain) {
    case "pet":
      if (route.includes("pet")) return generatePetDetailPage(name, desc);
      if (route.includes("schedule")) return generateSchedulePage(name, desc);
      if (route.includes("daily-log")) return generateDailyLogPage(name, desc);
      if (route.includes("album")) return generateAlbumPage(name, desc);
      if (route.includes("hospital")) return generateHospitalPage(name, desc);
      if (route.includes("profile")) return generateProfilePage(name, desc, "pet");
      break;

    case "ecommerce":
      if (route.includes("product")) return generateProductDetailPage(name, desc);
      if (route.includes("cart")) return generateCartPage(name, desc);
      if (route.includes("order")) return generateOrderPage(name, desc);
      if (route.includes("profile")) return generateProfilePage(name, desc, "ecommerce");
      break;

    case "note":
      if (route.includes("note")) return generateNoteEditorPage(name, desc);
      if (route.includes("tag")) return generateTagPage(name, desc);
      if (route.includes("settings")) return generateSettingsPage(name, desc);
      break;

    case "enterprise":
      if (route.includes("attendance")) return generateAttendancePage(name, desc);
      if (route.includes("approval")) return generateApprovalPage(name, desc);
      if (route.includes("contact")) return generateContactPage(name, desc);
      break;

    case "education":
      if (route.includes("course")) return generateCoursePage(name, desc);
      if (route.includes("learn")) return generateLearnPage(name, desc);
      if (route.includes("exercise")) return generateExercisePage(name, desc);
      if (route.includes("live")) return generateLivePage(name, desc);
      break;

    case "fitness":
      if (route.includes("checkin")) return generateCheckinPage(name, desc);
      if (route.includes("stat")) return generateStatsPage(name, desc);
      break;
  }

  // Generic fallback page
  return `"use client";

import { useState } from "react";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { EmptyState } from "@/components/ui/EmptyState";
${hookNameRaw ? `import { use${hookNameRaw} } from "@/hooks/use${hookNameRaw}";` : ""}

export default function ${sanitizeComponentName(name)}Page() {
  ${hookNameRaw ? `const { data, loading, error, refetch } = use${hookNameRaw}();` : ""}
  const [open, setOpen] = useState(false);

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-bold text-gray-800">${name}</h1>
          <p className="text-sm text-gray-500 mt-1">${desc}</p>
        </div>
        <Button onClick={() => setOpen(true)} variant="primary" size="sm">新增</Button>
      </div>

      {${hookNameRaw ? `loading ? (
        <div className="flex justify-center py-12">
          <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" />
        </div>
      ) : error ? (
        <Card className="text-center py-8">
          <p className="text-red-500 text-sm">{error}</p>
          <Button onClick={refetch} variant="secondary" size="sm" className="mt-2">重试</Button>
        </Card>
      ) : data.length === 0 ? (
        <EmptyState
          icon="📭"
          title="暂无数据"
          description="还没有任何记录，点击上方按钮开始"
          action={<Button onClick={() => setOpen(true)} variant="primary" size="sm">创建第一条</Button>}
        />
      ) : (
        <div className="space-y-3">
          {data.map((item: any) => (
            <Card key={item.id}>
              <h3 className="font-medium text-gray-800">{item.title || item.name || \`Item \${item.id.slice(0, 8)}\`}</h3>
              <p className="text-xs text-gray-400 mt-1">{item.createdAt || ""}</p>
            </Card>
          ))}
        </div>
      )}` : `<Card>
          <p className="text-gray-500 text-sm">${name} 页面内容</p>
          <p className="text-xs text-gray-400 mt-1">路由: ${route}</p>
        </Card>}`}
    </div>
  );
}
`;
}

// ── Domain-specific page generators ──────────────

function generatePetDetailPage(name, desc) {
  return `"use client";

import { useState, useEffect } from "react";
import { useParams } from "next/navigation";
import type { Pet, Schedule } from "@/types";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { PetCard } from "@/components/pet/PetCard";
import { ScheduleCard } from "@/components/pet/ScheduleCard";
import { EmptyState } from "@/components/ui/EmptyState";

export default function PetDetailPage() {
  const params = useParams();
  const [pet, setPet] = useState<Pet | null>(null);
  const [schedules, setSchedules] = useState<Schedule[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // TODO: Replace with actual API call
    const loadData = async () => {
      setLoading(true);
      // Simulated data
      setPet({
        id: params.id as string,
        ownerId: "u1",
        name: "毛球",
        species: "猫",
        breed: "英短",
        birthDate: "2023-03-15",
        weightKg: 4.5,
        avatarUrl: "",
        createdAt: new Date().toISOString(),
      });
      setSchedules([
        { id: "s1", petId: params.id as string, type: "vaccine", title: "年度疫苗", scheduledAt: "2026-07-01T09:00:00Z", completed: false },
        { id: "s2", petId: params.id as string, type: "deworming", title: "季度驱虫", scheduledAt: "2026-06-15T10:00:00Z", completed: false },
        { id: "s3", petId: params.id as string, type: "checkup", title: "体检", scheduledAt: "2026-05-20T14:00:00Z", completed: true },
      ]);
      setLoading(false);
    };
    loadData();
  }, [params.id]);

  if (loading) return <div className="flex justify-center py-16"><div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" /></div>;
  if (!pet) return <EmptyState icon="🐾" title="未找到宠物" description="该宠物可能已被删除" />;

  return (
    <div className="space-y-6">
      {/* Pet Profile */}
      <div className="bg-gradient-to-br from-amber-100 to-orange-100 rounded-2xl p-6 text-center">
        <div className="w-24 h-24 mx-auto rounded-full bg-white shadow flex items-center justify-center text-5xl">
          {pet.avatarUrl ? <img src={pet.avatarUrl} alt={pet.name} className="w-full h-full rounded-full object-cover" /> : "🐱"}
        </div>
        <h1 className="text-2xl font-bold text-gray-800 mt-3">{pet.name}</h1>
        <p className="text-sm text-gray-500">{pet.breed} · {pet.weightKg}kg · {pet.species}</p>
      </div>

      {/* Health Schedule */}
      <section>
        <h2 className="text-lg font-semibold text-gray-800 mb-3">健康日程</h2>
        <div className="space-y-2">
          {schedules.length > 0 ? schedules.map(s => <ScheduleCard key={s.id} schedule={s} />) : <EmptyState icon="📅" title="暂无日程" />}
        </div>
      </section>

      {/* Quick Info */}
      <div className="grid grid-cols-2 gap-3">
        <Card className="text-center">
          <p className="text-2xl font-bold text-gray-800">{pet.weightKg}</p>
          <p className="text-xs text-gray-500">体重 (kg)</p>
        </Card>
        <Card className="text-center">
          <p className="text-2xl font-bold text-gray-800">{new Date().getFullYear() - new Date(pet.birthDate).getFullYear()}岁</p>
          <p className="text-xs text-gray-500">年龄</p>
        </Card>
      </div>
    </div>
  );
}
`;
}

function generateSchedulePage(name, desc) {
  return `"use client";

import { useState } from "react";
import type { Schedule } from "@/types";
import { Button } from "@/components/ui/Button";
import { ScheduleCard } from "@/components/pet/ScheduleCard";
import { Modal } from "@/components/ui/Modal";
import { Input } from "@/components/ui/Input";
import { EmptyState } from "@/components/ui/EmptyState";

const mockSchedules: Schedule[] = [
  { id: "s1", petId: "p1", type: "vaccine", title: "年度疫苗加强针", scheduledAt: "2026-07-01T09:00:00Z", completed: false },
  { id: "s2", petId: "p1", type: "deworming", title: "季度驱虫", scheduledAt: "2026-06-15T10:00:00Z", completed: false },
  { id: "s3", petId: "p1", type: "checkup", title: "半年度体检", scheduledAt: "2026-05-20T14:00:00Z", completed: true },
  { id: "s4", petId: "p1", type: "grooming", title: "美容护理", scheduledAt: "2026-06-10T11:00:00Z", completed: false },
];

export default function SchedulePage() {
  const [schedules, setSchedules] = useState<Schedule[]>(mockSchedules);
  const [showModal, setShowModal] = useState(false);

  const toggleComplete = (id: string) => {
    setSchedules(prev => prev.map(s => s.id === id ? { ...s, completed: !s.completed } : s));
  };

  const pending = schedules.filter(s => !s.completed);
  const done = schedules.filter(s => s.completed);

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-bold text-gray-800">健康日程</h1>
          <p className="text-sm text-gray-500 mt-1">管理宠物的疫苗、驱虫、体检安排</p>
        </div>
        <Button onClick={() => setShowModal(true)} variant="primary" size="sm">+ 添加日程</Button>
      </div>

      {/* Pending */}
      <section>
        <h2 className="text-sm font-semibold text-gray-500 uppercase mb-2">待完成 ({pending.length})</h2>
        <div className="space-y-2">
          {pending.length > 0 ? pending.map(s => <ScheduleCard key={s.id} schedule={s} onToggle={() => toggleComplete(s.id)} />) : <EmptyState icon="✅" title="全部完成" />}
        </div>
      </section>

      {/* Completed */}
      {done.length > 0 && (
        <section>
          <h2 className="text-sm font-semibold text-gray-500 uppercase mb-2">已完成 ({done.length})</h2>
          <div className="space-y-2">
            {done.map(s => <ScheduleCard key={s.id} schedule={s} />)}
          </div>
        </section>
      )}

      <Modal open={showModal} onClose={() => setShowModal(false)} title="添加日程">
        <div className="space-y-3">
          <Input label="标题" placeholder="如：年度疫苗" />
          <Input label="日期" type="date" />
          <div className="flex gap-2 justify-end">
            <Button variant="secondary" onClick={() => setShowModal(false)}>取消</Button>
            <Button variant="primary" onClick={() => setShowModal(false)}>保存</Button>
          </div>
        </div>
      </Modal>
    </div>
  );
}
`;
}

function generateDailyLogPage(name, desc) {
  return `"use client";

import { useState } from "react";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { Input } from "@/components/ui/Input";
import { EmptyState } from "@/components/ui/EmptyState";

const CATEGORIES = [
  { key: "food", icon: "🍖", label: "饮食" },
  { key: "exercise", icon: "🏃", label: "运动" },
  { key: "excretion", icon: "💩", label: "排泄" },
  { key: "weight", icon: "⚖️", label: "体重" },
];

interface LogEntry {
  id: string;
  category: string;
  icon: string;
  value: string;
  time: string;
}

export default function DailyLogPage() {
  const [logs, setLogs] = useState<LogEntry[]>([]);
  const [category, setCategory] = useState("food");
  const [value, setValue] = useState("");

  const addLog = () => {
    if (!value.trim()) return;
    const cat = CATEGORIES.find(c => c.key === category)!;
    setLogs(prev => [{
      id: Date.now().toString(),
      category: cat.key,
      icon: cat.icon,
      value,
      time: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }),
    }, ...prev]);
    setValue("");
  };

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-xl font-bold text-gray-800">日常记录</h1>
        <p className="text-sm text-gray-500 mt-1">记录宠物的饮食、运动、排泄和体重变化</p>
      </div>

      {/* Quick Record */}
      <Card>
        <div className="space-y-3">
          <div className="flex gap-2">
            {CATEGORIES.map(c => (
              <button key={c.key} onClick={() => setCategory(c.key)}
                className={\`flex-1 py-2 rounded-lg text-sm text-center transition-colors \${category === c.key ? "bg-blue-100 text-blue-700 font-medium" : "bg-gray-50 text-gray-500"}\`}>
                {c.icon} {c.label}
              </button>
            ))}
          </div>
          <div className="flex gap-2">
            <Input placeholder="输入记录内容..." value={value} onChange={e => setValue(e.target.value)} onKeyDown={e => e.key === "Enter" && addLog()} />
            <Button onClick={addLog} size="sm">记录</Button>
          </div>
        </div>
      </Card>

      {/* Log Timeline */}
      <div className="space-y-2">
        {logs.length > 0 ? logs.map(log => (
          <div key={log.id} className="flex items-center gap-3 px-4 py-2 bg-white rounded-lg border border-gray-100">
            <span className="text-xl">{log.icon}</span>
            <span className="flex-1 text-sm text-gray-700">{log.value}</span>
            <span className="text-xs text-gray-400">{log.time}</span>
          </div>
        )) : <EmptyState icon="📝" title="暂无记录" description="使用上方表单记录宠物日常" />}
      </div>
    </div>
  );
}
`;
}

function generateAlbumPage(name, desc) {
  return `"use client";

import { useState } from "react";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { EmptyState } from "@/components/ui/EmptyState";

const mockPhotos = Array.from({ length: 8 }, (_, i) => ({
  id: \`p\${i}\`,
  color: ["#FEE2E2", "#FEF3C7", "#D1FAE5", "#DBEAFE", "#EDE9FE", "#FCE7F3", "#E0E7FF", "#FFEDD5"][i],
  emoji: ["🐱", "🐶", "🐱", "🐶", "🐱", "🐱", "🐶", "🐱"][i],
  date: new Date(2026, 5, i + 1).toISOString(),
}));

export default function AlbumPage() {
  const [photos] = useState(mockPhotos);

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-bold text-gray-800">成长相册</h1>
          <p className="text-sm text-gray-500 mt-1">记录宠物的成长瞬间</p>
        </div>
        <Button variant="primary" size="sm">+ 上传照片</Button>
      </div>

      <div className="grid grid-cols-3 gap-2">
        {photos.map(photo => (
          <div key={photo.id} className="aspect-square rounded-xl flex items-center justify-center text-4xl" style={{ backgroundColor: photo.color }}>
            {photo.emoji}
          </div>
        ))}
      </div>

      <p className="text-center text-xs text-gray-400">下拉加载更多照片</p>
    </div>
  );
}
`;
}

function generateHospitalPage(name, desc) {
  return `"use client";

import { useState } from "react";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { Input } from "@/components/ui/Input";

const mockHospitals = [
  { id: "h1", name: "宠颐生动物医院", address: "朝阳区建国路88号", phone: "010-88886666", rating: 4.8, distance: 1.2, services: ["疫苗", "体检", "手术", "美容"] },
  { id: "h2", name: "瑞鹏宠物医院", address: "海淀区中关村大街1号", phone: "010-66668888", rating: 4.6, distance: 2.8, services: ["急诊", "住院", "化验"] },
  { id: "h3", name: "佳雯宠物诊所", address: "西城区金融街16号", phone: "010-55556666", rating: 4.5, distance: 3.5, services: ["疫苗", "驱虫", "体检"] },
];

export default function HospitalPage() {
  const [search, setSearch] = useState("");

  const filtered = mockHospitals.filter(h =>
    h.name.includes(search) || h.address.includes(search)
  );

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-xl font-bold text-gray-800">附近医院</h1>
        <p className="text-sm text-gray-500 mt-1">查找附近的宠物医院</p>
      </div>

      <Input placeholder="搜索医院名称或地址..." value={search} onChange={e => setSearch(e.target.value)} />

      <div className="space-y-3">
        {filtered.map(h => (
          <Card key={h.id}>
            <div className="flex items-start gap-3">
              <div className="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center text-blue-500 flex-shrink-0">🏥</div>
              <div className="flex-1 min-w-0">
                <div className="flex items-center gap-2">
                  <h3 className="font-medium text-gray-800 truncate">{h.name}</h3>
                  <span className="text-xs px-1.5 py-0.5 bg-yellow-100 text-yellow-700 rounded flex-shrink-0">⭐ {h.rating}</span>
                </div>
                <p className="text-xs text-gray-500 mt-1">{h.address}</p>
                <p className="text-xs text-gray-400 mt-0.5">{h.distance}km · {h.phone}</p>
                <div className="flex gap-1 mt-2">
                  {h.services.map(s => <span key={s} className="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded">{s}</span>)}
                </div>
              </div>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}
`;
}

function generateProfilePage(name, desc, domain) {
  return `"use client";

import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";

const menuItems = [
  { icon: "⚙️", label: "设置", href: "#" },
  { icon: "🔔", label: "通知", href: "#" },
  { icon: "❓", label: "帮助与反馈", href: "#" },
  { icon: "📄", label: "关于我们", href: "#" },
];

export default function ProfilePage() {
  return (
    <div className="space-y-6">
      {/* User Info */}
      <div className="bg-gradient-to-br from-blue-500 to-blue-700 rounded-2xl p-6 text-white text-center">
        <div className="w-20 h-20 mx-auto rounded-full bg-white/20 flex items-center justify-center text-3xl backdrop-blur">😊</div>
        <h1 className="text-xl font-bold mt-3">用户昵称</h1>
        <p className="text-sm text-blue-100">138****1234</p>
        <Button variant="ghost" className="mt-3 bg-white/10 text-white hover:bg-white/20" size="sm">编辑资料</Button>
      </div>

      {/* Stats */}
      <div className="grid grid-cols-3 gap-3">
        ${domain === "pet" ? `<Card className="text-center"><p className="text-xl font-bold text-gray-800">3</p><p className="text-xs text-gray-500">我的宠物</p></Card>
        <Card className="text-center"><p className="text-xl font-bold text-gray-800">12</p><p className="text-xs text-gray-500">健康日程</p></Card>
        <Card className="text-center"><p className="text-xl font-bold text-gray-800">56</p><p className="text-xs text-gray-500">日常记录</p></Card>` : `<Card className="text-center"><p className="text-xl font-bold text-gray-800">0</p><p className="text-xs text-gray-500">收藏</p></Card>
        <Card className="text-center"><p className="text-xl font-bold text-gray-800">0</p><p className="text-xs text-gray-500">订单</p></Card>
        <Card className="text-center"><p className="text-xl font-bold text-gray-800">0</p><p className="text-xs text-gray-500">足迹</p></Card>`}
      </div>

      {/* Menu */}
      <Card className="divide-y divide-gray-50 p-0">
        {menuItems.map((item, i) => (
          <a key={i} href={item.href} className="flex items-center gap-3 px-4 py-3 hover:bg-gray-50 transition-colors">
            <span className="text-lg">{item.icon}</span>
            <span className="flex-1 text-sm text-gray-700">{item.label}</span>
            <svg className="w-4 h-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /></svg>
          </a>
        ))}
      </Card>
    </div>
  );
}
`;
}

// More domain-specific page generators... (abbreviated for space)
function generateProductDetailPage(name, desc) {
  return `"use client";
import { Button } from "@/components/ui/Button";

export default function ProductDetailPage() {
  return (
    <div className="space-y-4">
      <div className="aspect-square bg-gray-100 rounded-2xl flex items-center justify-center text-6xl">📦</div>
      <h1 className="text-xl font-bold">商品名称</h1>
      <p className="text-2xl font-bold text-red-500">¥99.00</p>
      <p className="text-sm text-gray-500">库存充足 · 24小时内发货</p>
      <Button variant="primary" className="w-full" size="lg">加入购物车</Button>
      <div className="space-y-2">
        <h3 className="font-semibold text-gray-700">商品详情</h3>
        <p className="text-sm text-gray-500 leading-relaxed">商品详细描述信息。这里会展示商品的全部规格参数、使用方法、注意事项等内容。</p>
      </div>
    </div>
  );
}`;
}

function generateCartPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";

export default function CartPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">购物车</h1>
      <Card><p className="text-sm text-gray-500">购物车功能开发中...</p></Card>
      <Button variant="primary" className="w-full" size="lg">去结算</Button>
    </div>
  );
}`;
}

function generateOrderPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";
import { EmptyState } from "@/components/ui/EmptyState";

export default function OrderPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">我的订单</h1>
      <EmptyState icon="📦" title="暂无订单" description="去逛逛吧" />
    </div>
  );
}`;
}

function generateNoteEditorPage(name, desc) {
  return `"use client";
import { useState } from "react";
import { Input } from "@/components/ui/Input";
import { Button } from "@/components/ui/Button";

export default function NoteEditorPage() {
  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <h1 className="text-xl font-bold text-gray-800">编辑笔记</h1>
        <Button variant="primary" size="sm">保存</Button>
      </div>
      <Input value={title} onChange={e => setTitle(e.target.value)} placeholder="笔记标题" className="text-lg font-medium border-0 px-0" />
      <textarea
        value={content}
        onChange={e => setContent(e.target.value)}
        placeholder="开始记录..."
        className="w-full min-h-[300px] text-sm text-gray-700 resize-none focus:outline-none"
      />
    </div>
  );
}`;
}

function generateTagPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";

const tags = ["工作", "个人", "灵感", "读书笔记", "代码片段", "日记"];
export default function TagPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">标签管理</h1>
      <div className="flex flex-wrap gap-2">
        {tags.map(tag => <span key={tag} className="px-3 py-1.5 bg-gray-100 rounded-full text-sm text-gray-600 cursor-pointer hover:bg-gray-200">{tag}</span>)}
      </div>
    </div>
  );
}`;
}

function generateAttendancePage(name, desc) {
  return `"use client";
import { useState } from "react";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";

export default function AttendancePage() {
  const [checkedIn, setCheckedIn] = useState(false);
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">考勤打卡</h1>
      <Card className="text-center py-8">
        <p className="text-4xl mb-2">{checkedIn ? "✅" : "🕐"}</p>
        <p className="text-sm text-gray-500">{new Date().toLocaleString("zh-CN")}</p>
        <Button onClick={() => setCheckedIn(!checkedIn)} variant={checkedIn ? "secondary" : "primary"} className="mt-4" size="lg" disabled={checkedIn}>
          {checkedIn ? "已打卡" : "打卡签到"}
        </Button>
      </Card>
    </div>
  );
}`;
}

function generateApprovalPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";

const approvals = [
  { id: "a1", type: "请假", applicant: "张三", status: "pending", date: "2026-06-05" },
  { id: "a2", type: "报销", applicant: "李四", status: "approved", date: "2026-06-04" },
  { id: "a3", type: "加班", applicant: "王五", status: "rejected", date: "2026-06-03" },
];
const statusMap: Record<string, { label: string; color: string }> = {
  pending: { label: "待审批", color: "bg-yellow-100 text-yellow-700" },
  approved: { label: "已通过", color: "bg-green-100 text-green-700" },
  rejected: { label: "已驳回", color: "bg-red-100 text-red-700" },
};

export default function ApprovalPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">我的审批</h1>
      {approvals.map(a => {
        const s = statusMap[a.status];
        return (
          <Card key={a.id}>
            <div className="flex items-center justify-between">
              <div>
                <p className="font-medium text-gray-800">{a.type}申请 — {a.applicant}</p>
                <p className="text-xs text-gray-400 mt-1">{a.date}</p>
              </div>
              <span className={\`text-xs px-2 py-1 rounded \${s.color}\`}>{s.label}</span>
            </div>
          </Card>
        );
      })}
    </div>
  );
}`;
}

function generateCoursePage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";

const courses = [
  { id: "c1", title: "React 19 完全指南", instructor: "张老师", price: 199, cover: "📘" },
  { id: "c2", title: "TypeScript 高级编程", instructor: "李老师", price: 149, cover: "📙" },
  { id: "c3", title: "Node.js 后端实战", instructor: "王老师", price: 249, cover: "📗" },
];

export default function CoursePage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">课程中心</h1>
      {courses.map(c => (
        <Card key={c.id} className="flex gap-3">
          <div className="w-20 h-20 rounded-lg bg-gray-100 flex items-center justify-center text-3xl flex-shrink-0">{c.cover}</div>
          <div>
            <h3 className="font-medium text-gray-800">{c.title}</h3>
            <p className="text-xs text-gray-500 mt-1">{c.instructor}</p>
            <p className="text-sm font-bold text-red-500 mt-2">¥{c.price}</p>
          </div>
        </Card>
      ))}
    </div>
  );
}`;
}

function generateExercisePage(name, desc) {
  return `"use client";

export default function ExercisePage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">题库练习</h1>
      <p className="text-sm text-gray-500">章节练习和模拟考试功能</p>
    </div>
  );
}`;
}

function generateLearnPage(name, desc) {
  return `"use client";

export default function LearnPage() {
  return (
    <div className="space-y-4">
      <div className="aspect-video bg-black rounded-xl flex items-center justify-center">
        <span className="text-white text-6xl">▶️</span>
      </div>
      <h1 className="text-lg font-bold text-gray-800">课程标题</h1>
      <p className="text-sm text-gray-500">第1节 · 基础知识</p>
    </div>
  );
}`;
}

function generateLivePage(name, desc) {
  return `"use client";

export default function LivePage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">直播课堂</h1>
      <p className="text-sm text-gray-500">课程直播功能</p>
    </div>
  );
}`;
}

function generateCheckinPage(name, desc) {
  return `"use client";
import { useState } from "react";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";

const exercises = ["跑步", "游泳", "骑行", "跳绳", "瑜伽", "力量训练"];
export default function CheckinPage() {
  const [selected, setSelected] = useState("");
  const [duration, setDuration] = useState("");
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">运动打卡</h1>
      <div className="grid grid-cols-3 gap-2">
        {exercises.map(e => <Button key={e} variant={selected === e ? "primary" : "secondary"} size="sm" onClick={() => setSelected(e)}>{e}</Button>)}
      </div>
      <Card>
        <input type="number" value={duration} onChange={e => setDuration(e.target.value)} placeholder="运动时长（分钟）" className="w-full px-3 py-2 border rounded-lg text-sm" />
        <Button variant="primary" className="w-full mt-3" disabled={!selected || !duration}>打卡</Button>
      </Card>
    </div>
  );
}`;
}

function generateStatsPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";

export default function StatsPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">运动统计</h1>
      <div className="grid grid-cols-2 gap-3">
        <Card className="text-center"><p className="text-2xl font-bold text-blue-500">12</p><p className="text-xs text-gray-500">本月打卡</p></Card>
        <Card className="text-center"><p className="text-2xl font-bold text-green-500">420</p><p className="text-xs text-gray-500">总运动时长</p></Card>
      </div>
    </div>
  );
}`;
}

function generateContactPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";
import { Input } from "@/components/ui/Input";

export default function ContactPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">通讯录</h1>
      <Input placeholder="搜索同事..." />
      <div className="space-y-2">
        {["张三 · 技术部", "李四 · 产品部", "王五 · 设计部"].map((n, i) => (
          <Card key={i}><p className="text-sm text-gray-700">{n}</p></Card>
        ))}
      </div>
    </div>
  );
}`;
}

function generateSettingsPage(name, desc) {
  return `"use client";
import { Card } from "@/components/ui/Card";

const settings = ["账户安全", "通知设置", "主题偏好", "数据管理", "关于应用"];
export default function SettingsPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-xl font-bold text-gray-800">设置</h1>
      <Card className="divide-y p-0">
        {settings.map((s, i) => <div key={i} className="px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer flex items-center justify-between">{s} <span className="text-gray-300">›</span></div>)}
      </Card>
    </div>
  );
}`;
}

// ═══════════════════════════════════════════════════════
// 6. Layout & Config Generators
// ═══════════════════════════════════════════════════════

function generateLayout(prd) {
  const pages = prd.pages || [];
  const domain = prd.domain || "generic";
  const projectName = prd.projectName || "App";

  // Build navigation items from pages
  const navItems = pages.slice(0, 5).map((p, i) => {
    const route = p.route;
    const dirPath = route.replace(/^\//, "").replace(/:(\w+)/g, "[$1]").replace(/\/$/, "");
    const icons = ["🏠", "📋", "📅", "✏️", "📸", "🏥", "👤", "📚", "💬"];
    return { name: p.name, href: `/${dirPath}`, icon: icons[i] || "📌" };
  });

  return `import type { Metadata } from "next";
import "./globals.css";
import { Sidebar } from "@/components/layout/Sidebar";
import { BottomNav } from "@/components/layout/BottomNav";

export const metadata: Metadata = {
  title: "${projectName}",
  description: "${prd.summary || projectName}",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-CN">
      <body className="bg-gray-50 min-h-screen">
        <div className="flex">
          {/* Desktop Sidebar */}
          <Sidebar items={${JSON.stringify(navItems.map(({ name, href, icon }) => ({ name, href, icon })))} } projectName="${projectName}" />
          {/* Main Content */}
          <main className="flex-1 lg:pl-64 pb-20 lg:pb-0">
            <div className="max-w-2xl mx-auto px-4 py-6">
              {children}
            </div>
          </main>
        </div>
        {/* Mobile Bottom Nav */}
        <BottomNav items={${JSON.stringify(navItems.slice(0, 4).map(({ name, href, icon }) => ({ name, href, icon })))} } />
      </body>
    </html>
  );
}
`;
}

function generateSidebar(prd) {
  return `"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";

interface NavItem {
  name: string;
  href: string;
  icon: string;
}

interface SidebarProps {
  items: NavItem[];
  projectName: string;
}

export function Sidebar({ items, projectName }: SidebarProps) {
  const pathname = usePathname();

  return (
    <aside className="hidden lg:flex lg:flex-col lg:w-64 lg:fixed lg:inset-y-0 bg-white border-r border-gray-100">
      <div className="px-6 py-5 border-b border-gray-50">
        <h1 className="text-lg font-bold text-gray-800">{projectName}</h1>
      </div>
      <nav className="flex-1 px-3 py-4 space-y-1 overflow-y-auto">
        {items.map((item) => {
          const active = pathname === item.href;
          return (
            <Link
              key={item.href}
              href={item.href}
              className={\`flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm transition-colors \${
                active ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-600 hover:bg-gray-50"
              }\`}
            >
              <span className="text-lg">{item.icon}</span>
              <span>{item.name}</span>
            </Link>
          );
        })}
      </nav>
    </aside>
  );
}
`;
}

function generateBottomNav(prd) {
  return `"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";

interface NavItem {
  name: string;
  href: string;
  icon: string;
}

export function BottomNav({ items }: { items: NavItem[] }) {
  const pathname = usePathname();

  if (!items.length) return null;

  return (
    <nav className="lg:hidden fixed bottom-0 inset-x-0 bg-white border-t border-gray-100 z-40">
      <div className="flex items-center justify-around h-16">
        {items.map((item) => {
          const active = pathname === item.href;
          return (
            <Link
              key={item.href}
              href={item.href}
              className={\`flex flex-col items-center gap-0.5 px-3 py-1 \${
                active ? "text-blue-600" : "text-gray-400"
              }\`}
            >
              <span className="text-xl">{item.icon}</span>
              <span className="text-[10px]">{item.name}</span>
            </Link>
          );
        })}
      </div>
    </nav>
  );
}
`;
}

function generateConfigFiles(prd) {
  return {
    "package.json": JSON.stringify({
      name: (prd.projectName || "app").toLowerCase(),
      version: "0.1.0",
      private: true,
      scripts: {
        dev: "next dev",
        build: "next build",
        start: "next start",
        lint: "next lint",
        "type-check": "tsc --noEmit",
      },
      dependencies: {
        next: "^15.0.0",
        react: "^19.0.0",
        "react-dom": "^19.0.0",
      },
      devDependencies: {
        "@types/node": "^22.0.0",
        "@types/react": "^19.0.0",
        typescript: "^5.6.0",
        tailwindcss: "^3.4.0",
        postcss: "^8.4.0",
        autoprefixer: "^10.4.0",
      },
    }, null, 2),

    "tsconfig.json": JSON.stringify({
      compilerOptions: {
        target: "ES2017",
        lib: ["dom", "dom.iterable", "esnext"],
        allowJs: true,
        skipLibCheck: true,
        strict: true,
        noEmit: true,
        esModuleInterop: true,
        module: "esnext",
        moduleResolution: "bundler",
        resolveJsonModule: true,
        isolatedModules: true,
        jsx: "preserve",
        incremental: true,
        plugins: [{ name: "next" }],
        paths: { "@/*": ["./*"] },
      },
      include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
      exclude: ["node_modules"],
    }, null, 2),

    "next.config.ts": `import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;
`,

    "tailwind.config.ts": `import type { Config } from "tailwindcss";

const config: Config = {
  content: ["./app/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", "./hooks/**/*.{js,ts,jsx,tsx,mdx}", "./services/**/*.{js,ts,jsx,tsx,mdx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};
export default config;
`,

    "postcss.config.js": `module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};
`,

    "app/globals.css": `@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
`,
  };
}

// ═══════════════════════════════════════════════════════
// 7. Main Builder Pipeline
// ═══════════════════════════════════════════════════════

/**
 * Sanitize component name
 */
function sanitizeComponentName(name) {
  // Remove non-ASCII letters/digits and ensure valid JS identifier
  const clean = name.replace(/[^a-zA-Z0-9_]/g, "") || "Page";
  return clean.charAt(0).toUpperCase() + clean.slice(1);
}

/**
 * Build the complete frontend project.
 *
 * @param {object} prd — PRD from SF-01
 * @param {object} arch — Architecture from SF-02
 * @returns {{ files: Record<string, string>, stats: object }}
 */
export function buildFrontend(prd, arch) {
  if (!prd || prd.error) {
    return { error: "INVALID_PRD", message: prd?.message || "Invalid PRD input" };
  }
  if (!arch || arch.error) {
    return { error: "INVALID_ARCH", message: arch?.message || "Invalid Architecture input" };
  }

  // Create contract from arch (single source of truth)
  const contract = arch.contract || createContract(prd, { databaseSchema: arch.databaseSchema || [], domain: arch.domain });

  const files = {};

  // 1. Types (from contract)
  const types = generateTypes(prd, arch, contract);
  files["types/index.ts"] = types;

  // 2. Config files
  const configs = generateConfigFiles(prd);
  Object.assign(files, configs);

  // 3. Layout
  const layout = generateLayout(prd);
  files["app/layout.tsx"] = layout;

  // 4. Layout components
  files["components/layout/Sidebar.tsx"] = generateSidebar(prd);
  files["components/layout/BottomNav.tsx"] = generateBottomNav(prd);

  // 5. UI components
  const components = generateComponents(prd, arch);
  Object.assign(files, components);

  // 6. Pages
  const pages = generatePages(prd, arch);
  Object.assign(files, pages);

  // 7. Hooks
  const hooks = generateHooks(prd, arch);
  Object.assign(files, hooks);

  // 8. API services
  const services = generateApiServices(prd, arch);
  Object.assign(files, services);

  const fileList = Object.keys(files);
  const pageFiles = fileList.filter(f => f.includes("/page.tsx"));
  const componentFiles = fileList.filter(f => f.includes("components/"));
  const hookFiles = fileList.filter(f => f.includes("hooks/"));
  const serviceFiles = fileList.filter(f => f.includes("services/"));

  return {
    files,
    stats: {
      totalFiles: fileList.length,
      pages: pageFiles.length,
      components: componentFiles.length,
      hooks: hookFiles.length,
      services: serviceFiles.length,
      pageFiles,
      componentFiles,
    },
  };
}

// ═══════════════════════════════════════════════════════
// 8. File I/O
// ═══════════════════════════════════════════════════════

export function loadJSON(path) {
  try {
    if (!existsSync(path)) return { data: null, error: `File not found: ${path}` };
    return { data: JSON.parse(readFileSync(path, "utf-8")), error: null };
  } catch (e) {
    return { data: null, error: `Failed to load: ${e.message}` };
  }
}

export function writeFrontend(result, outputDir) {
  mkdirSync(outputDir, { recursive: true });
  for (const [relPath, content] of Object.entries(result.files)) {
    const fullPath = resolve(outputDir, relPath);
    mkdirSync(dirname(fullPath), { recursive: true });
    writeFileSync(fullPath, content);
  }
}

// ═══════════════════════════════════════════════════════
// 9. CLI Entry
// ═══════════════════════════════════════════════════════

function parseArgs() {
  const args = process.argv.slice(2);
  const opts = { prd: null, arch: null, inputText: null, output: null, verbose: false, help: false };
  for (let i = 0; i < args.length; i++) {
    if (args[i] === "--prd" && args[i + 1]) opts.prd = args[++i];
    else if (args[i] === "--arch" && args[i + 1]) opts.arch = args[++i];
    else if (args[i] === "--input-text" && args[i + 1]) opts.inputText = args[++i];
    else if (args[i] === "--output" && args[i + 1]) opts.output = args[++i];
    else if (args[i] === "--verbose") opts.verbose = true;
    else if (args[i] === "--help" || args[i] === "-h") opts.help = true;
  }
  return opts;
}

async function main() {
  const opts = parseArgs();

  if (opts.help) {
    console.log(`
Frontend Builder Agent — SF-03

Usage:
  node scripts/frontend-builder-agent.mjs --prd <path> --arch <path> [options]
  node scripts/frontend-builder-agent.mjs --input-text "<需求>" [options]

Options:
  --prd <path>          PRD JSON（SF-01 输出）
  --arch <path>         Architecture JSON（SF-02 输出）
  --input-text <text>   直接传入需求（自动调 SF-01 → SF-02 → SF-03）
  --output <dir>        输出目录（default: frontend/）
  --verbose             详细输出
  --help                显示帮助
`);
    return;
  }

  let prd = null, arch = null;

  if (opts.prd && opts.arch) {
    const prdRes = loadJSON(opts.prd);
    const archRes = loadJSON(opts.arch);
    if (prdRes.error) { console.error(prdRes.error); process.exit(1); }
    if (archRes.error) { console.error(archRes.error); process.exit(1); }
    prd = prdRes.data;
    arch = archRes.data;
  } else if (opts.inputText) {
    try {
      const sf01 = await import("./project-intake-agent.mjs");
      const sf02 = await import("./architecture-agent.mjs");
      prd = sf01.generatePRD(opts.inputText);
      if (prd.error) { console.error(`SF-01: ${prd.message}`); process.exit(1); }
      arch = sf02.generateArchitecture(prd);
      if (arch.error) { console.error(`SF-02: ${arch.message}`); process.exit(1); }
    } catch (e) {
      console.error(`Pipeline error: ${e.message}`);
      process.exit(1);
    }
  }

  if (!prd || !arch) {
    console.error("Error: --prd + --arch or --input-text is required. Use --help.");
    process.exit(1);
  }

  const result = buildFrontend(prd, arch);
  if (result.error) { console.error(result.message); process.exit(1); }

  const outputDir = opts.output ? resolve(opts.output) : DEFAULT_OUTPUT;
  writeFrontend(result, outputDir);

  if (opts.verbose) {
    console.error(`Project: ${prd.projectName}`);
    console.error(`Files: ${result.stats.totalFiles}`);
    console.error(`Pages: ${result.stats.pages}, Components: ${result.stats.components}, Hooks: ${result.stats.hooks}, Services: ${result.stats.services}`);
  }

  console.log(JSON.stringify({
    projectName: prd.projectName,
    outputDir,
    stats: result.stats,
  }, null, 2));
}

if (process.argv[1] && (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1]))) {
  main();
}
