/**
 * ClipboardText Component Generator
 *
 * Generates a single ClipboardText ComponentSet in Figma with size property.
 * Reads variant definitions from component-registry.json (the source of truth).
 *
 * ## Component Structure
 * ClipboardText is a compound component with:
 * - Container with input-like styling (bg-secondary, ring-border, rounded)
 * - Text span with monospace font showing the copyable text
 * - Ghost button with clipboard icon on the right (separated by border)
 *
 * ## Size Variants
 * - sm: Extra small for compact UIs (text-xs, button size sm)
 * - base: Default size (text-sm, button size base)
 * - lg: Large for prominent display (text-sm, button size lg)
 */

import {
  bindFillToVariable,
  bindStrokeToVariable,
  getVariableByName,
  createModeSection,
  createRowLabel,
  bindTextColorToVariable,
  SECTION_PADDING,
  SECTION_GAP,
  GRID_LAYOUT,
  FALLBACK_VALUES,
  SECTION_LAYOUT,
  SECTION_TITLE,
  COLORS,
  VAR_NAMES,
} from "./shared";
import { createIconInstance, bindIconColor } from "./icon-utils";
import { parseTailwindClasses } from "../parsers/tailwind-to-figma";
import { logComplete } from "../logger";

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

// Extract ClipboardText component from registry
const clipboardTextComponent = registry.components.ClipboardText as any;
const clipboardTextProps = clipboardTextComponent.props;
const clipboardTextStyling = clipboardTextComponent.styling;

const sizeProp = clipboardTextProps.size as {
  values: string[];
  classes: Record<string, string>;
  descriptions: Record<string, string>;
  default: string;
};

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

/**
 * Get all variant data (for snapshot testing)
 * Returns intermediate data before Figma API calls
 */
export function getAllVariantData() {
  const baseStyles = getBaseStyles();
  const sizeConfig = getClipboardTextSizeConfig();

  // Generate size-specific configs for all sizes
  const sizes = sizeConfig.values.map((size) => {
    const config = getSizeConfig(size);
    const inputClasses = getInputSizeClasses(size);

    return {
      size,
      height: config.height,
      classes: config.classes,
      buttonSize: config.buttonSize,
      dimensions: config.dimensions,
      inputClasses,
    };
  });

  return {
    sizeConfig,
    baseStyles,
    sizes,
    stylingConfig: clipboardTextStyling,
  };
}

/**
 * Get base styles from registry
 * Returns input base styles and clipboard text base styles
 */
export function getBaseStyles() {
  const inputBase = clipboardTextStyling.inputStyles.base as string;
  const textTokens = clipboardTextStyling.states.text as string[];

  return {
    input: {
      raw: inputBase,
      parsed: parseTailwindClasses(inputBase),
    },
    text: textTokens,
  };
}

/**
 * Get complete size configuration for a given size
 * Returns height, classes, buttonSize, and dimensions from sizeVariants
 */
export function getSizeConfig(size: string) {
  let sizeVariant = clipboardTextStyling.sizeVariants[size];

  return {
    height: sizeVariant.height as number,
    classes: sizeVariant.classes as string,
    buttonSize: sizeVariant.buttonSize as string,
    dimensions: sizeVariant.dimensions as {
      paddingX: number;
      gap: number;
      borderRadius: number;
      fontSize: number;
    },
  };
}

/**
 * Get parsed Tailwind classes for input size
 * Returns parsed styles from inputStyles.sizes
 */
export function getInputSizeClasses(size: string) {
  const config = getSizeConfig(size);
  const buttonSize = config.buttonSize;

  // Get input size classes for the button size (sm->sm, base->base, lg->lg)
  const inputSizeClasses = clipboardTextStyling.inputStyles.sizes[
    buttonSize
  ] as string | undefined;

  if (!inputSizeClasses) {
    // Fallback to base size
    const baseClasses = clipboardTextStyling.inputStyles.sizes.base as string;
    return parseTailwindClasses(baseClasses);
  }

  return parseTailwindClasses(inputSizeClasses);
}

/**
 * Get clipboard text size config from registry
 * Returns size prop values, default, classes, and descriptions
 */
export function getClipboardTextSizeConfig() {
  return {
    values: sizeProp.values,
    default: sizeProp.default,
    classes: sizeProp.classes,
    descriptions: sizeProp.descriptions,
  };
}

/**
 * Create a single ClipboardText component with the specified size
 */
async function createClipboardTextComponent(
  size: string,
): Promise<ComponentNode> {
  const sizeClasses = sizeProp.classes[size] || "";
  const description = sizeProp.descriptions[size] || "";

  // Get configuration from registry
  const sizeConfig = getSizeConfig(size);
  const baseStyles = getBaseStyles();
  const inputSizeStyles = getInputSizeClasses(size);

  // Parse additional styles
  const inputBaseStyles = baseStyles.input.parsed;
  const textSizeStyles = parseTailwindClasses(sizeClasses);

  // Parse clipboard text base styles (bg-kumo-base font-mono)
  const clipboardTextStyles = baseStyles.text.join(" ");
  const clipboardStyles = parseTailwindClasses(clipboardTextStyles);

  // Create component
  const component = figma.createComponent();
  component.name = "size=" + size;
  component.description = description;

  // Set up auto-layout (horizontal: text + button)
  component.layoutMode = "HORIZONTAL";
  component.primaryAxisAlignItems = "CENTER";
  component.counterAxisAlignItems = "CENTER";
  component.itemSpacing = 0; // No gap between text and button
  component.paddingLeft = 0;
  component.paddingRight = 0;
  component.paddingTop = 0;
  component.paddingBottom = 0;
  component.primaryAxisSizingMode = "AUTO";
  component.counterAxisSizingMode = "AUTO";

  // Apply corner radius from size config dimensions
  component.cornerRadius = sizeConfig.dimensions.borderRadius;

  // Apply fill - ClipboardText uses bg-kumo-base (overrides input's bg-kumo-control)
  if (clipboardStyles.fillVariable) {
    const fillVar = getVariableByName(clipboardStyles.fillVariable);
    if (fillVar) {
      bindFillToVariable(component, fillVar.id);
    }
  } else if (inputBaseStyles.fillVariable) {
    const fillVar = getVariableByName(inputBaseStyles.fillVariable);
    if (fillVar) {
      bindFillToVariable(component, fillVar.id);
    }
  }

  // Apply border (ring-kumo-line from input)
  if (inputBaseStyles.strokeVariable) {
    const strokeVar = getVariableByName(inputBaseStyles.strokeVariable);
    if (strokeVar) {
      bindStrokeToVariable(component, strokeVar.id, 1);
    }
  }

  // Create text container frame (holds the text with padding)
  const textFrame = figma.createFrame();
  textFrame.name = "TextContainer";
  textFrame.layoutMode = "HORIZONTAL";
  textFrame.primaryAxisAlignItems = "CENTER";
  textFrame.counterAxisAlignItems = "CENTER";
  textFrame.primaryAxisSizingMode = "AUTO";
  textFrame.counterAxisSizingMode = "FIXED";
  textFrame.fills = []; // Transparent
  textFrame.paddingLeft = sizeConfig.dimensions.paddingX;
  textFrame.paddingRight = sizeConfig.dimensions.paddingX;

  // Set height from size config
  textFrame.resize(100, sizeConfig.height);

  // Create text node with monospace font
  const fontSize =
    textSizeStyles.fontSize ||
    inputSizeStyles.fontSize ||
    FALLBACK_VALUES.fontSize;
  const textNode = figma.createText();

  // Load monospace font
  await figma.loadFontAsync({ family: "Roboto Mono", style: "Regular" });

  textNode.characters = "npm install @cloudflare/kumo";
  textNode.fontSize = fontSize;
  textNode.fontName = { family: "Roboto Mono", style: "Regular" };
  textNode.name = "Text";

  // Apply text color (text-kumo-default from input)
  if (inputBaseStyles.textVariable) {
    const textVar = getVariableByName(inputBaseStyles.textVariable);
    if (textVar) {
      bindTextColorToVariable(textNode, textVar.id);
    }
  }

  textFrame.appendChild(textNode);
  component.appendChild(textFrame);

  // Create button frame (ghost button with icon)
  const buttonFrame = figma.createFrame();
  buttonFrame.name = "CopyButton";
  buttonFrame.layoutMode = "HORIZONTAL";
  buttonFrame.primaryAxisAlignItems = "CENTER";
  buttonFrame.counterAxisAlignItems = "CENTER";
  buttonFrame.primaryAxisSizingMode = "FIXED";
  buttonFrame.counterAxisSizingMode = "FIXED";
  buttonFrame.fills = []; // Transparent (ghost button)

  // Button padding from size config
  const buttonPadding = sizeConfig.dimensions.paddingX;
  buttonFrame.paddingLeft = buttonPadding;
  buttonFrame.paddingRight = buttonPadding;

  // Set button size based on size config height
  buttonFrame.resize(
    buttonPadding * 2 + 16, // padding + icon size
    sizeConfig.height,
  );

  // Add left border to button (border-l border-kumo-fill)
  const borderColorVar = getVariableByName(VAR_NAMES.color.line);
  if (borderColorVar) {
    buttonFrame.strokeLeftWeight = 1;
    buttonFrame.strokeTopWeight = 0;
    buttonFrame.strokeRightWeight = 0;
    buttonFrame.strokeBottomWeight = 0;
    buttonFrame.strokes = [
      {
        type: "SOLID",
        color: COLORS.borderGray,
      },
    ];
    // Bind stroke to variable
    let stroke: SolidPaint = {
      type: "SOLID",
      color: COLORS.borderGray,
    };
    stroke = figma.variables.setBoundVariableForPaint(
      stroke,
      "color",
      borderColorVar,
    );
    buttonFrame.strokes = [stroke];
  }

  // Create clipboard icon
  const iconSize = 16;
  const iconInstance = createIconInstance("ph-clipboard", iconSize);

  if (iconInstance) {
    iconInstance.name = "Icon";
    // Bind icon color to text-kumo-default (inherits from parent in real component)
    bindIconColor(iconInstance, VAR_NAMES.text.default);
    buttonFrame.appendChild(iconInstance);
  } else {
    // Fallback: create placeholder
    const iconPlaceholder = figma.createRectangle();
    iconPlaceholder.name = "Icon (placeholder)";
    iconPlaceholder.resize(iconSize, iconSize);
    iconPlaceholder.cornerRadius = 2;
    iconPlaceholder.fills = [{ type: "SOLID", color: COLORS.placeholder }];
    buttonFrame.appendChild(iconPlaceholder);
  }

  component.appendChild(buttonFrame);

  return component;
}

/**
 * Generate ClipboardText ComponentSet with size property
 *
 * Creates a single "ClipboardText" ComponentSet with sizes derived from
 * component-registry.json. Creates both light and dark mode sections.
 *
 * @param startY - Y position to start placing the section
 * @returns The Y position after this section (for next section placement)
 */
export async function generateClipboardTextComponents(
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  // Get size keys from the registry
  const sizes = sizeProp.values;
  const components: ComponentNode[] = [];

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

  // Layout spacing - vertical layout with labels
  const rowGap = GRID_LAYOUT.rowGap.compact;
  const labelColumnWidth = GRID_LAYOUT.labelColumnWidth.small;

  // Track position for laying out components vertically
  let currentY = 0;

  for (let i = 0; i < sizes.length; i++) {
    const size = sizes[i];
    const component = await createClipboardTextComponent(size);

    // Record row label
    rowLabels.push({ y: currentY, text: "size=" + size });

    // Position each component vertically with label offset
    component.x = labelColumnWidth;
    component.y = currentY;
    currentY += component.height + rowGap;
    components.push(component);
  }

  // Combine all variants into a single ComponentSet
  // @ts-ignore - combineAsVariants works at runtime
  const componentSet = figma.combineAsVariants(components, figma.currentPage);
  componentSet.name = "ClipboardText";
  componentSet.description =
    "ClipboardText component for displaying and copying text";

  // 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(
    figma.currentPage,
    "ClipboardText",
    "light",
  );
  lightSection.frame.resize(
    contentWidth + SECTION_PADDING * 2,
    contentHeight + SECTION_PADDING * 2 + contentYOffset,
  );

  // Create dark mode section
  const darkSection = createModeSection(
    figma.currentPage,
    "ClipboardText",
    "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 (const label of rowLabels) {
    const labelNode = await createRowLabel(
      label.text,
      SECTION_PADDING,
      SECTION_PADDING +
        contentYOffset +
        label.y +
        GRID_LAYOUT.labelVerticalOffset.md, // +8 to vertically center
    );
    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 + contentYOffset;
    darkSection.frame.appendChild(instance);
  }

  // Add row labels to dark section
  for (const label of rowLabels) {
    const labelNode = await createRowLabel(
      label.text,
      SECTION_PADDING,
      SECTION_PADDING +
        contentYOffset +
        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 + 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 ClipboardText ComponentSet with " +
      sizes.length +
      " variants (light + dark)",
  );

  return startY + totalHeight + SECTION_GAP;
}
