/**
 * CodeBlock Component Generator
 *
 * Generates a single CodeBlock ComponentSet in Figma with lang property variants.
 * Reads variant definitions from component-registry.json (the source of truth).
 *
 * CodeBlock is a Tier 2 component (wrapper around Code):
 * - Same lang variants as Code (ts, tsx, jsonc, bash, css)
 * - Adds container styling: border, background, padding
 * - Wraps monospace text inside a frame
 */

import {
  createTextNode,
  bindFillToVariable,
  bindStrokeToVariable,
  getVariableByName,
  createModeSection,
  createRowLabel,
  bindTextColorToVariable,
  SECTION_PADDING,
  SECTION_GAP,
  FALLBACK_VALUES,
  GRID_LAYOUT,
  SECTION_LAYOUT,
  SECTION_TITLE,
  VAR_NAMES,
} from "./shared";
// Note: Line height for code uses FALLBACK_VALUES.lineHeight.code (20px)
import { parseTailwindClasses } from "../parsers/tailwind-to-figma";
import { logComplete } from "../logger";

// Import variant data from the registry (generated by build:ai-metadata)
// CodeBlock uses the same lang variants as Code
import registry from "@cloudflare/kumo/ai/component-registry.json";
import themeData from "../generated/theme-data.json";

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

/**
 * CodeBlock wrapper styles from code-block.tsx
 * Container: "min-w-0 rounded-md border border-kumo-fill bg-kumo-base [&>pre]:p-2.5!"
 */
const CODE_BLOCK_WRAPPER_STYLES =
  "min-w-0 rounded-md border border-kumo-fill bg-kumo-base";
const CODE_INNER_PADDING = themeData.tailwind.spacing.scale["2.5"]; // p-2.5 = 10px

/**
 * TESTABLE EXPORTS - Pure functions that return intermediate data
 * These functions compute data without calling Figma APIs, enabling snapshot tests.
 */

/**
 * Get base styles for CodeBlock container
 * Returns container styling and inner padding info
 */
export function getBaseStyles() {
  const codeComponent = registry.components.Code as any;
  const codeStyling = codeComponent.styling;
  const containerStyles = (
    codeStyling.states.code_block_container as string[]
  ).join(" ");

  return {
    container: {
      raw: containerStyles,
      parsed: parseTailwindClasses(containerStyles),
    },
    innerPadding: CODE_INNER_PADDING, // p-2.5 = 10px
  };
}

/**
 * Get container configuration with dimensions and styling
 * Returns structured config for Figma container creation
 */
export function getContainerConfig() {
  const codeComponent = registry.components.Code as any;
  const codeStyling = codeComponent.styling;
  const containerStyles = (
    codeStyling.states.code_block_container as string[]
  ).join(" ");
  const parsed = parseTailwindClasses(containerStyles);

  return {
    borderRadius: parsed.borderRadius || FALLBACK_VALUES.borderRadius.medium,
    padding: CODE_INNER_PADDING,
    border: {
      hasBorder: parsed.hasBorder || false,
      strokeWeight: parsed.strokeWeight || FALLBACK_VALUES.strokeWeight,
      strokeVariable: parsed.strokeVariable || "",
    },
    fill: {
      fillVariable: parsed.fillVariable || "",
    },
  };
}

/**
 * Get complete intermediate data for all variants
 * Returns containerStyles + all lang variant data (for snapshot testing)
 */
export function getAllVariantData() {
  const codeComponent = registry.components.Code as any;
  const codeStyling = codeComponent.styling;
  const containerStyles = (
    codeStyling.states.code_block_container as string[]
  ).join(" ");
  const parsed = parseTailwindClasses(containerStyles);

  return {
    containerStyles: {
      raw: containerStyles,
      parsed,
    },
    variants: langProp.values.map((lang) => ({
      lang,
      description: langProp.descriptions[lang] || "",
      placeholderText: getPlaceholderText(lang),
    })),
  };
}

/**
 * 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 CodeBlock component with the specified lang variant
 */
async function createCodeBlockComponent(lang: string): Promise<ComponentNode> {
  const description = langProp.descriptions[lang] || "";

  // Parse wrapper styles
  const wrapperStyles = parseTailwindClasses(CODE_BLOCK_WRAPPER_STYLES);

  // Create component with auto-layout
  const component = figma.createComponent();
  component.name = "lang=" + lang;
  component.description = description;

  // Set up auto-layout for frame container
  component.layoutMode = "VERTICAL";
  component.primaryAxisAlignItems = "MIN";
  component.counterAxisAlignItems = "MIN";
  component.paddingLeft = CODE_INNER_PADDING;
  component.paddingRight = CODE_INNER_PADDING;
  component.paddingTop = CODE_INNER_PADDING;
  component.paddingBottom = CODE_INNER_PADDING;
  component.primaryAxisSizingMode = "AUTO";
  component.counterAxisSizingMode = "AUTO";
  component.cornerRadius =
    wrapperStyles.borderRadius || FALLBACK_VALUES.borderRadius.medium;

  // Apply background (bg-kumo-base)
  if (wrapperStyles.fillVariable) {
    const fillVar = getVariableByName(wrapperStyles.fillVariable);
    if (fillVar) {
      bindFillToVariable(component, fillVar.id);
    }
  }

  // Apply border (border-kumo-fill)
  if (wrapperStyles.strokeVariable) {
    const strokeVar = getVariableByName(wrapperStyles.strokeVariable);
    if (strokeVar) {
      bindStrokeToVariable(component, strokeVar.id, 1);
    }
  }

  // Create monospace text node
  const textNode = await createTextNode(getPlaceholderText(lang), 14, 400);
  textNode.name = "Code";

  // 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-strong 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-strong doesn't exist
    const surfaceVar = getVariableByName(VAR_NAMES.text.default);
    if (surfaceVar) {
      bindTextColorToVariable(textNode, surfaceVar.id);
    }
  }

  // Set line height - uses FALLBACK_VALUES.lineHeight.code (20px from code.tsx leading-[20px])
  textNode.lineHeight = {
    value: FALLBACK_VALUES.lineHeight.code,
    unit: "PIXELS",
  };

  component.appendChild(textNode);

  return component;
}

/**
 * Generate CodeBlock ComponentSet with lang property
 */
export async function generateCodeBlockComponents(
  page: PageNode,
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  figma.currentPage = page;

  const langs = langProp.values;
  const components: ComponentNode[] = [];
  const rowLabels: { y: number; text: string }[] = [];

  const rowGap = 50;
  const labelColumnWidth = 160;
  let currentY = 0;

  for (let i = 0; i < langs.length; i++) {
    const lang = langs[i];
    const component = await createCodeBlockComponent(lang);

    rowLabels.push({ y: currentY, text: "lang=" + lang });

    component.x = labelColumnWidth;
    component.y = currentY;
    currentY = currentY + component.height + rowGap;
    components.push(component);
  }

  // @ts-ignore - combineAsVariants works at runtime
  const componentSet = figma.combineAsVariants(components, page);
  componentSet.name = "CodeBlock";
  componentSet.description = "CodeBlock component with lang variants";
  componentSet.layoutMode = "NONE";

  const contentWidth = componentSet.width + labelColumnWidth;
  const contentHeight = componentSet.height;

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

  const lightSection = createModeSection(page, "CodeBlock", "light");
  lightSection.frame.resize(
    contentWidth + SECTION_PADDING * 2,
    contentHeight + SECTION_PADDING * 2 + contentYOffset,
  );

  const darkSection = createModeSection(page, "CodeBlock", "dark");
  darkSection.frame.resize(
    contentWidth + SECTION_PADDING * 2,
    contentHeight + SECTION_PADDING * 2 + contentYOffset,
  );

  // Add title inside each frame

  lightSection.frame.appendChild(componentSet);
  componentSet.x = SECTION_PADDING + labelColumnWidth;
  componentSet.y = SECTION_PADDING + contentYOffset;

  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,
    );
    lightSection.frame.appendChild(labelNode);
  }

  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);
  }

  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);
  }

  const totalWidth = contentWidth + SECTION_PADDING * 2;
  const totalHeight = contentHeight + SECTION_PADDING * 2 + contentYOffset;

  lightSection.frame.resize(totalWidth, totalHeight);
  darkSection.frame.resize(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 CodeBlock ComponentSet with " +
      langs.length +
      " variants (light + dark)",
  );

  return startY + totalHeight + SECTION_GAP;
}
