/**
 * Serialize persisted design-kit state for system prompts and MCP `projects.get`.
 */

import fs from "fs";
import path from "path";
import { getStarterKit } from "./registry";
import { resolveKitTokens } from "./resolve";
import type { ResolvedKitTokens } from "./resolve";
import type { DesignKitOverrides, DesignKitPersisted } from "./types";
import { DEFAULT_DESIGN_KIT_ID, UI_FOUNDATION_LABELS } from "./types";

export function parsePersistedDesignKit(
  raw: unknown,
): DesignKitPersisted | null {
  if (raw === undefined || raw === null) return null;
  if (typeof raw !== "object" || Array.isArray(raw)) return null;
  const r = raw as Record<string, unknown>;
  const kitId =
    typeof r.kitId === "string" && r.kitId.trim()
      ? r.kitId.trim()
      : DEFAULT_DESIGN_KIT_ID;
  const perKit: Record<string, DesignKitOverrides> = {};
  if (r.perKit && typeof r.perKit === "object" && !Array.isArray(r.perKit)) {
    for (const [key, val] of Object.entries(r.perKit)) {
      if (!val || typeof val !== "object" || Array.isArray(val)) continue;
      const v = val as Record<string, unknown>;
      const o: DesignKitOverrides = {};
      if (typeof v.accentHex === "string") o.accentHex = v.accentHex;
      if (typeof v.radiusMdPx === "number" && Number.isFinite(v.radiusMdPx)) {
        o.radiusMdPx = v.radiusMdPx;
      }
      if (v.fontPreset === "inter" || v.fontPreset === "system") {
        o.fontPreset = v.fontPreset;
      }
      if (v.density === "compact" || v.density === "comfortable") {
        o.density = v.density;
      }
      perKit[key] = o;
    }
  }
  return { kitId, perKit };
}

/** Resolved tokens + hints for codegen; null if no kit saved or starter id unknown. */
export function buildDesignKitToolPayload(
  projectData: {
    designKit?: unknown;
  } | null,
): {
  kitId: string;
  starterKitName: string;
  uiFoundation: string;
  foundationLabel: string;
  foundationNotesForAi: string;
  overrides: DesignKitOverrides;
  resolved: ResolvedKitTokens;
  applyNote: string;
  designMdContent: string;
  tokensCssContent: string;
} | null {
  const persisted = parsePersistedDesignKit(projectData?.designKit);
  if (!persisted) return null;
  const kit = getStarterKit(persisted.kitId);
  if (!kit) return null;
  const overrides = persisted.perKit[persisted.kitId] ?? {};
  const resolved = resolveKitTokens(kit, overrides);

  const SYSTEMS_DIR = path.join(
    process.cwd(),
    "lib",
    "scaffold",
    "open-design",
    "design-systems",
  );
  let designMdContent = "";
  let tokensCssContent = "";
  try {
    const mdPath = path.join(SYSTEMS_DIR, kit.id, "DESIGN.md");
    if (fs.existsSync(mdPath)) {
      designMdContent = fs.readFileSync(mdPath, "utf-8");
    }
    const cssPath = path.join(SYSTEMS_DIR, kit.id, "tokens.css");
    if (fs.existsSync(cssPath)) {
      tokensCssContent = fs.readFileSync(cssPath, "utf-8");
    }
  } catch (err) {
    console.error("Failed to read design kit files:", err);
  }

  return {
    kitId: persisted.kitId,
    starterKitName: kit.name,
    uiFoundation: kit.uiFoundation,
    foundationLabel: UI_FOUNDATION_LABELS[kit.uiFoundation],
    foundationNotesForAi: kit.foundationNotesForAi,
    overrides,
    resolved,
    applyNote:
      "Saving theme on the Design tab updates project metadata only, not the repo. Wire tokens with fs_* (e.g. :root CSS variables, Tailwind theme.extend, MUI createTheme, HeroUI theme). If many hard-coded colors exist and no shared theme layer, tell the user honestly that matching the kit may require a refactor toward centralized tokens.",
    designMdContent,
    tokensCssContent,
  };
}

/** Markdown block appended to the active-project section of the chat system prompt. */
export function buildDesignKitPromptSection(
  activeProject: { designKit?: unknown } | null | undefined,
): string {
  const payload = buildDesignKitToolPayload(activeProject ?? null);
  if (!payload) return "";

  const {
    starterKitName,
    kitId,
    overrides,
    resolved,
    uiFoundation,
    foundationLabel,
    foundationNotesForAi,
    designMdContent,
    tokensCssContent,
  } = payload;
  let section = `
## Design kit / theme (authoritative for UI styling)

The founder configured the visual language on the **Design** tab. Treat **resolved** values below as the source of truth when editing frontend code under this project's repo (\`/workspace/<slug>/\`).

- **Starter kit:** ${starterKitName} (\`${kitId}\`)
- **UI foundation:** ${foundationLabel} (\`${uiFoundation}\`)
- **Foundation guidance (follow when adding components):** ${foundationNotesForAi}
- **Saved overrides:** ${JSON.stringify(overrides)}
- **Accent (base):** \`${resolved.accentHex}\`
- **Accent ramp (12 steps):** ${JSON.stringify(resolved.accentScale)}
- **Neutral ramp (12 steps):** ${JSON.stringify(resolved.grayScale)}
- **Radius (px):** xs=${resolved.radiusXs}, sm=${resolved.radiusSm}, md=${resolved.radiusMdPx}, xl=${resolved.radiusXl}, xxl=${resolved.radiusXxl}
- **Font stack:** ${resolved.fontFamily}
- **Density:** ${resolved.density}

**Applying theme:** The Design tab does **not** mutate files. When the user saves theme changes or asks the app to match the kit, update the codebase's theme layer (whatever stack they use — locate it with \`fs_grep\` / \`fs_read\`). If matching would require touching many unrelated components because colors are inlined everywhere, **say so explicitly** and describe that a **central-token refactor** is needed before the UI can fully align — do not imply the preview alone updates production code.
`.trimStart();

  if (designMdContent) {
    section += `\n\n### Design Guidelines (from DESIGN.md)\n\n${designMdContent}`;
  }
  if (tokensCssContent) {
    section += `\n\n### Tokens (from tokens.css)\n\n\`\`\`css\n${tokensCssContent}\n\`\`\``;
  }

  return section;
}
