import { logComplete } from "../logger";
/**
 * Text Component Generator
 *
 * Generates a single Text ComponentSet in Figma with properties:
 * - variant: heading1, heading2, heading3, body, secondary, success, error, mono, mono-secondary
 * - size: xs, sm, base, lg (only applies to body/secondary/success/error variants)
 *
 * Reads variant definitions from component-registry.json (the source of truth).
 *
 * ## Implementation Notes
 *
 * The Text component is a typography primitive that renders text with consistent styling.
 * Unlike Button or Badge, Text components are purely typographic - no backgrounds or borders.
 *
 * ### Variant Categories (derived from text.tsx source)
 * - Headings (heading1, heading2, heading3): Fixed sizes, semibold weight, no size prop
 * - Copy (body, secondary, success, error): Supports size variants
 * - Monospace (mono, mono-secondary): Special optical sizing (lg→base, default→sm)
 *
 * ### Base Styling
 * All text variants get `text-kumo-default` as the base color, then variant classes override.
 */

import {
  createTextNode,
  getVariableByName,
  createModeSection,
  createRowLabel,
  createColumnHeaders,
  bindTextColorToVariable,
  SECTION_PADDING,
  SECTION_GAP,
  GRID_LAYOUT,
  FALLBACK_VALUES,
  SECTION_LAYOUT,
  VAR_NAMES,
} from "./shared";
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 textProps = registry.components.Text.props;
const variantProp = textProps.variant as {
  values: string[];
  classes: Record<string, string>;
  descriptions: Record<string, string>;
  default: string;
};
const sizeProp = textProps.size as {
  values: string[];
  classes: Record<string, string>;
  descriptions: Record<string, string>;
  default: string;
};

/**
 * Base text class applied to all Text components (from text.tsx line 178)
 */
const TEXT_BASE_CLASS = "text-kumo-default";

/**
 * Variant categories derived from text.tsx source code (lines 162-163)
 * These determine which variants support size and special optical sizing
 */
const COPY_VARIANTS = ["body", "secondary", "success", "error"];
const MONO_VARIANTS = ["mono", "mono-secondary"];

/**
 * Check if variant is a copy variant (supports size)
 */
function isCopyVariant(variant: string): boolean {
  return COPY_VARIANTS.includes(variant);
}

/**
 * Check if variant is a monospace variant (needs optical sizing)
 */
function isMonoVariant(variant: string): boolean {
  return MONO_VARIANTS.includes(variant);
}

/**
 * Get placeholder text based on variant
 */
function getPlaceholderText(variant: string): string {
  if (variant.startsWith("heading")) {
    return (
      variant.charAt(0).toUpperCase() + variant.slice(1).replace(/(\d)/, " $1")
    );
  }
  if (isMonoVariant(variant)) {
    return "const code = true;";
  }
  return "Text content";
}

/**
 * Create a single Text component for a variant/size combination
 *
 * Follows the logic from text.tsx _Text function (lines 149-195)
 */
async function createTextComponent(
  variant: string,
  size: string | null,
): Promise<ComponentNode> {
  const variantClasses = variantProp.classes[variant] || "";
  const variantDesc = variantProp.descriptions[variant] || "";

  // Determine effective size classes based on variant type
  // From text.tsx lines 180-186:
  // - Copy variants: use the size prop directly
  // - Mono variants: lg→base, default→sm (optical sizing)
  // - Headings: no size classes
  let effectiveSizeClasses = "";
  let sizeDesc = "";

  if (isCopyVariant(variant) && size) {
    effectiveSizeClasses = sizeProp.classes[size] || "";
    sizeDesc = sizeProp.descriptions[size] || "";
  } else if (isMonoVariant(variant)) {
    // Monospace optical sizing from text.tsx lines 183-186
    if (size === "lg") {
      effectiveSizeClasses = sizeProp.classes["base"] || "";
      sizeDesc = "Large text (optically adjusted to base)";
    } else {
      effectiveSizeClasses = sizeProp.classes["sm"] || "";
      sizeDesc = "Default text (optically adjusted to small)";
    }
  }
  // Headings: no size classes applied

  // Combine base + variant + size classes for parsing
  const combinedClasses =
    `${TEXT_BASE_CLASS} ${variantClasses} ${effectiveSizeClasses}`.trim();

  // Parse all styles using the tailwind parser
  const styles = parseTailwindClasses(combinedClasses);

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

  // Name format: variant=X or variant=X, size=Y
  if (size) {
    component.name = "variant=" + variant + ", size=" + size;
    component.description = variantDesc + ". " + sizeDesc;
  } else {
    component.name = "variant=" + variant;
    component.description = variantDesc;
  }

  // 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 text components
  component.fills = [];

  // Determine font weight from parsed styles
  // font-semibold = 600, font-medium = 500, default = 400
  let fontWeight = 400;
  if (variantClasses.includes("font-semibold")) {
    fontWeight = 600;
  } else if (variantClasses.includes("font-medium")) {
    fontWeight = 500;
  }

  // Create text node with parsed font size
  const fontSize = styles.fontSize || FALLBACK_VALUES.fontSize; // Default to base (16px)
  const textNode = await createTextNode(
    getPlaceholderText(variant),
    fontSize,
    fontWeight,
  );
  textNode.name = "Text";

  // Apply text color
  // Priority: variant-specific color > base text-kumo-default color
  if (styles.textVariable) {
    const textVar = getVariableByName(styles.textVariable);
    if (textVar) {
      bindTextColorToVariable(textNode, textVar.id);
    }
  } else {
    // Fallback to base text-kumo-default color
    const surfaceVar = getVariableByName(VAR_NAMES.text.default);
    if (surfaceVar) {
      bindTextColorToVariable(textNode, surfaceVar.id);
    }
  }

  component.appendChild(textNode);

  return component;
}

/**
 * Generate Text ComponentSet with variant and size properties
 *
 * Creates a single "Text" 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 generateTextComponents(
  page: PageNode,
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  figma.currentPage = page;

  // Get variant and size keys from the registry
  const variants = variantProp.values;
  const sizes = sizeProp.values;

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

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

  // Track column headers: { x, text }
  let columnHeaders: { x: number; text: string }[] = [];
  let columnHeadersRecorded = false;

  // Layout configuration - using centralized GRID_LAYOUT constants from shared.ts
  const componentGap = GRID_LAYOUT.componentGapX.standard; // 24px
  const rowHeight = GRID_LAYOUT.rowGap.standard; // 48px (closest to 50)
  const headerRowHeight = GRID_LAYOUT.headerRowHeight;
  const labelColumnWidth = GRID_LAYOUT.labelColumnWidth.standard;
  let currentRow = 0;

  // Generate variants based on their category
  for (let i = 0; i < variants.length; i++) {
    const variant = variants[i];

    // Record row label (offset by header row)
    rowLabels.push({
      y: currentRow * rowHeight + headerRowHeight,
      text: "variant=" + variant,
    });

    if (isCopyVariant(variant)) {
      // Copy variants: generate all size combinations
      let currentX = labelColumnWidth;
      for (let j = 0; j < sizes.length; j++) {
        const component = await createTextComponent(variant, sizes[j]);
        component.x = currentX;
        component.y = currentRow * rowHeight + headerRowHeight;

        // Record column headers from first copy variant row
        if (!columnHeadersRecorded) {
          columnHeaders.push({ x: currentX, text: "size=" + sizes[j] });
        }

        currentX = currentX + component.width + componentGap;
        components.push(component);
      }
      if (!columnHeadersRecorded) {
        columnHeadersRecorded = true;
      }
      currentRow++;
    } else if (isMonoVariant(variant)) {
      // Mono variants: only default (null) and lg sizes
      // From text.tsx type definition (lines 119-122): size?: "lg"
      let currentX = labelColumnWidth;

      // Default size (no size prop)
      const defaultComponent = await createTextComponent(variant, null);
      defaultComponent.x = currentX;
      defaultComponent.y = currentRow * rowHeight + headerRowHeight;
      currentX = currentX + defaultComponent.width + componentGap;
      components.push(defaultComponent);

      // lg size only
      const lgComponent = await createTextComponent(variant, "lg");
      lgComponent.x = currentX;
      lgComponent.y = currentRow * rowHeight + headerRowHeight;
      components.push(lgComponent);

      currentRow++;
    } else {
      // Headings: no size variants (from text.tsx lines 125-129: size?: never)
      const component = await createTextComponent(variant, null);
      component.x = labelColumnWidth;
      component.y = currentRow * rowHeight + headerRowHeight;
      components.push(component);
      currentRow++;
    }
  }

  // Combine all into a single ComponentSet
  // @ts-ignore - combineAsVariants works at runtime
  const componentSet = figma.combineAsVariants(components, page);
  componentSet.name = "Text";
  componentSet.description =
    "Text component with variant and size properties for typography";

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

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

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

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

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

  // Add column headers to light section
  await createColumnHeaders(
    columnHeaders.map((h) => ({ x: h.x + SECTION_PADDING, text: h.text })),
    SECTION_PADDING,
    lightSection.frame,
  );

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

  // Create instances for dark section
  // Note: component positions are relative to ComponentSet after combineAsVariants
  // We need to add labelColumnWidth to match the light section layout
  for (const component of components) {
    const instance = component.createInstance();
    instance.x = component.x + SECTION_PADDING + labelColumnWidth;
    instance.y = component.y + SECTION_PADDING + headerRowHeight;
    darkSection.frame.appendChild(instance);
  }

  // Add column headers to dark section
  await createColumnHeaders(
    columnHeaders.map((h) => ({ x: h.x + SECTION_PADDING, text: h.text })),
    SECTION_PADDING,
    darkSection.frame,
  );

  // Add row labels to dark section
  for (const label of rowLabels) {
    const labelNode = await createRowLabel(
      label.text,
      SECTION_PADDING,
      SECTION_PADDING + label.y + GRID_LAYOUT.labelVerticalOffset.md,
    );
    darkSection.frame.appendChild(labelNode);
  }

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

  lightSection.frame.resize(totalWidth, totalHeight);
  darkSection.frame.resize(totalWidth, totalHeight);

  // Add title inside each frame

  // Position sections side by side
  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 Text ComponentSet with " +
      components.length +
      " variants (light + dark)",
  );

  return startY + totalHeight + SECTION_GAP;
}

/**
 * Exports for testing - derived from registry
 */
export const TEXT_VARIANTS_EXPORT = variantProp.values;
export const TEXT_SIZES_EXPORT = sizeProp.values;

/**
 * Testable export functions for tests
 *
 * These functions expose intermediate data structures for testing
 * without requiring Figma plugin runtime. They follow the pattern
 * established in badge.ts and code.ts generators.
 *
 * Source of truth chain:
 * text.tsx → component-registry.json → text.ts (generator) → Figma
 */

/**
 * Get base styles from registry with parsed data
 */
export function getBaseStyles() {
  const raw = TEXT_BASE_CLASS;
  const parsed = parseTailwindClasses(raw);
  return {
    raw,
    parsed,
  };
}

/**
 * Get variant configuration from registry
 */
export function getVariantConfig() {
  return {
    variants: variantProp.values,
    sizes: sizeProp.values,
    variantClasses: variantProp.classes,
    variantDescriptions: variantProp.descriptions,
    sizeClasses: sizeProp.classes,
    sizeDescriptions: sizeProp.descriptions,
    defaultVariant: variantProp.default,
    defaultSize: sizeProp.default,
  };
}

/**
 * Get all variant data with parsed styles for testing
 *
 * This function returns the complete intermediate data structure
 * that the generator uses to create Figma components. It includes:
 * - Base styles (raw + parsed)
 * - Variant configuration
 * - Complete variant data for all variant/size combinations
 */
export function getAllVariantData() {
  const baseStyles = getBaseStyles();
  const variantConfig = getVariantConfig();

  // Build complete variant data (same logic as generator)
  const variants: Array<{
    variant: string;
    size: string | null;
    variantClasses: string;
    sizeClasses: string;
    combinedClasses: string;
    parsed: ReturnType<typeof parseTailwindClasses>;
    description: string;
    isCopyVariant: boolean;
    isMonoVariant: boolean;
  }> = [];

  for (const variant of variantConfig.variants) {
    const variantClasses = variantConfig.variantClasses[variant] || "";
    const variantDesc = variantConfig.variantDescriptions[variant] || "";
    const isCopy = isCopyVariant(variant);
    const isMono = isMonoVariant(variant);

    if (isCopy) {
      // Copy variants: all sizes
      for (const size of variantConfig.sizes) {
        const sizeClasses = variantConfig.sizeClasses[size] || "";
        const sizeDesc = variantConfig.sizeDescriptions[size] || "";
        const combinedClasses =
          `${TEXT_BASE_CLASS} ${variantClasses} ${sizeClasses}`.trim();
        const parsed = parseTailwindClasses(combinedClasses);

        variants.push({
          variant,
          size,
          variantClasses,
          sizeClasses,
          combinedClasses,
          parsed,
          description: `${variantDesc}. ${sizeDesc}`,
          isCopyVariant: true,
          isMonoVariant: false,
        });
      }
    } else if (isMono) {
      // Mono variants: default (null) and lg only
      // Default size
      const defaultSizeClasses = variantConfig.sizeClasses["sm"] || "";
      const defaultCombined =
        `${TEXT_BASE_CLASS} ${variantClasses} ${defaultSizeClasses}`.trim();
      const defaultParsed = parseTailwindClasses(defaultCombined);

      variants.push({
        variant,
        size: null,
        variantClasses,
        sizeClasses: defaultSizeClasses,
        combinedClasses: defaultCombined,
        parsed: defaultParsed,
        description: `${variantDesc}. Default text (optically adjusted to small)`,
        isCopyVariant: false,
        isMonoVariant: true,
      });

      // lg size
      const lgSizeClasses = variantConfig.sizeClasses["base"] || "";
      const lgCombined =
        `${TEXT_BASE_CLASS} ${variantClasses} ${lgSizeClasses}`.trim();
      const lgParsed = parseTailwindClasses(lgCombined);

      variants.push({
        variant,
        size: "lg",
        variantClasses,
        sizeClasses: lgSizeClasses,
        combinedClasses: lgCombined,
        parsed: lgParsed,
        description: `${variantDesc}. Large text (optically adjusted to base)`,
        isCopyVariant: false,
        isMonoVariant: true,
      });
    } else {
      // Headings: no size variants
      const combinedClasses = `${TEXT_BASE_CLASS} ${variantClasses}`.trim();
      const parsed = parseTailwindClasses(combinedClasses);

      variants.push({
        variant,
        size: null,
        variantClasses,
        sizeClasses: "",
        combinedClasses,
        parsed,
        description: variantDesc,
        isCopyVariant: false,
        isMonoVariant: false,
      });
    }
  }

  return {
    baseStyles,
    variantConfig,
    variants,
  };
}
