/**
 * Code Component Generator
 *
 * Generates a single Code ComponentSet in Figma with properties:
 * - lang: ts, tsx, jsonc, bash, css
 *
 * Reads variant definitions from component-registry.json (the source of truth).
 *
 * ## Implementation Notes
 *
 * The Code component is a simple typography primitive for displaying code snippets.
 * It has no backgrounds or borders (transparent), uses monospace font, and text-kumo-subtle color.
 *
 * ### Variant Categories
 * - lang variants: Only used for semantic meaning, no visual styling differences
 *
 * ### Base Styling (from code.tsx line 45)
 * "m-0 w-auto rounded-none border-none bg-transparent p-0 font-mono text-sm leading-[20px] text-kumo-subtle"
 */

import {
  createTextNode,
  getVariableByName,
  createModeSection,
  createRowLabel,
  bindTextColorToVariable,
  VAR_NAMES,
  SECTION_PADDING,
  SECTION_GAP,
  SECTION_LAYOUT,
  SECTION_TITLE,
  FONT_SIZE,
  FALLBACK_VALUES,
  GRID_LAYOUT,
} from "./shared";
import { logComplete } from "../logger";
import { parseTailwindClasses } from "../parsers/tailwind-to-figma";

// Import variant data from the registry (generated by build:ai-metadata)
import registry from "@cloudflare/kumo/ai/component-registry.json";

const codeProps = registry.components.Code.props;
const langProp = codeProps.lang as unknown as {
  values: string[];
  descriptions: Record<string, string>;
  default: string;
};

/**
 * Get placeholder text based on lang variant
 */
function getPlaceholderText(lang: string): string {
  if (lang === "bash") {
    return "npm install @cloudflare/kumo";
  }
  if (lang === "jsonc") {
    return '{ "key": "value" }';
  }
  if (lang === "css") {
    return ".class { color: blue; }";
  }
  if (lang === "tsx") {
    return "<Button>Click</Button>";
  }
  return 'const hello = "world";';
}

/**
 * Create a single Code component for a lang variant
 */
async function createCodeComponent(lang: string): Promise<ComponentNode> {
  const langDesc = langProp.descriptions[lang] || "";

  // Create component
  const component = figma.createComponent();

  // Name format: lang=X
  component.name = "lang=" + lang;
  component.description = langDesc;

  // Set up auto-layout (minimal - just wrapping text)
  component.layoutMode = "HORIZONTAL";
  component.primaryAxisAlignItems = "MIN";
  component.counterAxisAlignItems = "MIN";
  component.primaryAxisSizingMode = "AUTO";
  component.counterAxisSizingMode = "AUTO";

  // No background or border for code components
  component.fills = [];

  // Create text node with monospace font
  const fontSize = FONT_SIZE.base; // 14px from theme-kumo.css
  const fontWeight = FALLBACK_VALUES.fontWeight.normal; // 400
  const textNode = await createTextNode(
    getPlaceholderText(lang),
    fontSize,
    fontWeight,
  );
  textNode.name = "Text";

  // Load monospace font (Roboto Mono is reliably available in Figma)
  await figma.loadFontAsync({ family: "Roboto Mono", style: "Regular" });
  textNode.fontName = { family: "Roboto Mono", style: "Regular" };

  // Apply text color - text-kumo-subtle with fallback to text-kumo-default
  const labelVar = getVariableByName(VAR_NAMES.text.label);
  if (labelVar) {
    bindTextColorToVariable(textNode, labelVar.id);
  } else {
    // Fallback to text-kumo-default if text-kumo-subtle doesn't exist
    const surfaceVar = getVariableByName(VAR_NAMES.text.default);
    if (surfaceVar) {
      bindTextColorToVariable(textNode, surfaceVar.id);
    }
  }

  component.appendChild(textNode);

  return component;
}

/**
 * Generate Code ComponentSet with lang property
 *
 * Creates a single "Code" ComponentSet with variants derived from
 * component-registry.json. Creates both light and dark mode sections.
 *
 * @param page - Target page for components
 * @param startY - Y position to start placing sections
 * @returns The Y position after all sections (for next section placement)
 */
export async function generateCodeComponents(
  page: PageNode,
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  figma.currentPage = page;

  // Get lang keys from the registry
  const langs = langProp.values;

  // Generate components
  const components: ComponentNode[] = [];

  // Track row labels: { y, text }
  const rowLabels: { y: number; text: string }[] = [];

  // Layout configuration
  const rowHeight = 40;
  const labelColumnWidth = 160; // Space for labels on the left

  // Generate lang variants vertically
  for (let i = 0; i < langs.length; i++) {
    const lang = langs[i];

    // Record row label
    rowLabels.push({
      y: i * rowHeight,
      text: "lang=" + lang,
    });

    const component = await createCodeComponent(lang);
    component.x = labelColumnWidth;
    component.y = i * rowHeight;
    components.push(component);
  }

  // Combine all into a single ComponentSet
  // @ts-ignore - combineAsVariants works at runtime
  const componentSet = figma.combineAsVariants(components, page);
  componentSet.name = "Code";
  componentSet.description =
    "Code component with lang property for displaying code snippets";

  // Disable auto-layout on the ComponentSet
  componentSet.layoutMode = "NONE";

  // Calculate content dimensions (add label column width)
  const contentWidth = componentSet.width + labelColumnWidth;
  const contentHeight = componentSet.height;

  // Content Y offset to make room for title inside frame
  const contentYOffset = SECTION_TITLE.height;

  // Create light mode section
  const lightSection = createModeSection(page, "Code", "light");
  lightSection.frame.resize(
    contentWidth + SECTION_PADDING * 2,
    contentHeight + SECTION_PADDING * 2 + contentYOffset,
  );

  // Create dark mode section
  const darkSection = createModeSection(page, "Code", "dark");
  darkSection.frame.resize(
    contentWidth + SECTION_PADDING * 2,
    contentHeight + SECTION_PADDING * 2 + contentYOffset,
  );

  // Add title inside each frame

  // Move ComponentSet into light section frame
  lightSection.frame.appendChild(componentSet);
  componentSet.x = SECTION_PADDING + labelColumnWidth;
  componentSet.y = SECTION_PADDING + contentYOffset;

  // Add row labels to light section
  for (let li = 0; li < rowLabels.length; li++) {
    const label = rowLabels[li];
    const labelNode = await createRowLabel(
      label.text,
      SECTION_PADDING,
      SECTION_PADDING +
        contentYOffset +
        label.y +
        GRID_LAYOUT.labelVerticalOffset.md, // +8 to vertically center with text
    );
    lightSection.frame.appendChild(labelNode);
  }

  // Create instances for dark section
  for (let ci = 0; ci < components.length; ci++) {
    const comp = components[ci];
    const instance = comp.createInstance();
    instance.x = comp.x + SECTION_PADDING + labelColumnWidth;
    instance.y = comp.y + SECTION_PADDING + contentYOffset;
    darkSection.frame.appendChild(instance);
  }

  // Add row labels to dark section
  for (let di = 0; di < rowLabels.length; di++) {
    const darkLabel = rowLabels[di];
    const darkLabelNode = await createRowLabel(
      darkLabel.text,
      SECTION_PADDING,
      SECTION_PADDING + contentYOffset + darkLabel.y + 8,
    );
    darkSection.frame.appendChild(darkLabelNode);
  }

  // Resize sections to fit content with padding
  const totalWidth = contentWidth + SECTION_PADDING * 2;
  const totalHeight = contentHeight + SECTION_PADDING * 2 + contentYOffset;

  lightSection.section.resizeWithoutConstraints(totalWidth, totalHeight);
  darkSection.section.resizeWithoutConstraints(totalWidth, totalHeight);

  // Position sections at startY (no title offset needed since title is inside)
  lightSection.frame.x = SECTION_LAYOUT.startX;
  lightSection.frame.y = startY;

  darkSection.frame.x =
    lightSection.frame.x + totalWidth + SECTION_LAYOUT.modeGap;
  darkSection.frame.y = startY;

  logComplete(
    "Generated Code ComponentSet with " +
      components.length +
      " variants (light + dark)",
  );

  return startY + totalHeight + SECTION_GAP;
}

/**
 * Exports for testing - derived from registry
 */
export const CODE_LANGS_EXPORT = langProp.values;

/**
 * GREEN PHASE TDD - Testable exports implementation
 *
 * These functions return intermediate data derived from component-registry.json
 * for testing without Figma API calls. They enable snapshot tests to catch
 * unintended changes in parsing or layout logic.
 */

const codeComponent = registry.components.Code as any;
const codeStyling = codeComponent.styling as {
  baseTokens: string[];
  typography: {
    fontFamily: string;
    fontSize: string;
    lineHeight: string;
  };
  dimensions: {
    margin: string;
    padding: string;
    width: string;
  };
  appearance: {
    borderRadius: string;
    border: string;
    background: string;
  };
};

/**
 * Get base styles for Code component
 * Combines dimensions, borderRadius, base state classes, and baseTokens into a single string
 * @returns Object with raw base styles string and parsed Tailwind classes
 */
export function getBaseStyles() {
  // Combine all base style parts from the registry structure
  // Registry has: typography (fontFamily, fontSize, lineHeight), dimensions (margin, padding, width), appearance (borderRadius, border, background)
  const baseStyleParts = [
    codeStyling.typography?.fontFamily || "",
    codeStyling.typography?.fontSize || "",
    codeStyling.typography?.lineHeight || "",
    codeStyling.dimensions?.margin || "",
    codeStyling.dimensions?.padding || "",
    codeStyling.dimensions?.width || "",
    codeStyling.appearance?.borderRadius || "",
    codeStyling.appearance?.border || "",
    codeStyling.appearance?.background || "",
    ...codeStyling.baseTokens, // ["text-kumo-subtle"]
  ].filter(Boolean);

  const raw = baseStyleParts.join(" ");

  return {
    raw: raw,
    parsed: parseTailwindClasses(raw),
  };
}

/**
 * Get lang config from registry
 * @returns Lang variant configuration with values, descriptions, and default
 */
export function getLangConfig() {
  return {
    values: langProp.values,
    descriptions: langProp.descriptions,
    default: langProp.default,
  };
}

/**
 * Get all variant data for Code component
 * Returns complete intermediate data for all lang variants including base styles,
 * lang config, and variant-specific data.
 * @returns Complete intermediate data including base styles, lang config, and variants
 */
export function getAllVariantData() {
  const baseStyles = getBaseStyles();
  const langConfig = getLangConfig();

  // Build variant data for each lang
  const variants = langConfig.values.map(function (lang) {
    return {
      lang: lang,
      description: langConfig.descriptions[lang],
    };
  });

  return {
    baseStyles: baseStyles,
    langConfig: langConfig,
    variants: variants,
  };
}
