/**
 * Loader Component Generator
 *
 * Generates a Loader ComponentSet in Figma that matches
 * the Loader component structure from loader.tsx.
 *
 * Uses loader-data.json generated by build-loader-data.ts to stay
 * in sync with the actual component code.
 *
 * Sizes: sm (16px), base (24px), lg (32px)
 *
 * Note: Figma doesn't support SVG animations, so this shows a static
 * representation at the animation midpoint.
 *
 * @see packages/kumo/src/components/loader/loader.tsx
 * @see packages/kumo-figma/src/build-loader-data.ts
 */

import loaderData from "../generated/loader-data.json";
import {
  getVariableByName,
  createModeSection,
  createRowLabel,
  bindStrokeToVariable,
  SECTION_PADDING,
  SECTION_GAP,
  SECTION_LAYOUT,
  SECTION_TITLE,
  GRID_LAYOUT,
  COLORS,
  VAR_NAMES,
} from "./shared";
import { logComplete, logStart, logProgress } from "../logger";

/**
 * Create a single Loader component using figma.createNodeFromSvg()
 *
 * @param size - Size key (sm, base, lg)
 * @returns ComponentNode for the Loader
 */
async function createLoaderComponent(size: string): Promise<ComponentNode> {
  const sizeConfig = loaderData.sizes[size as keyof typeof loaderData.sizes];
  const sizeValue = sizeConfig.value;

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

  // Set up frame
  component.resize(sizeValue, sizeValue);
  component.fills = [];
  component.layoutMode = "HORIZONTAL";
  component.primaryAxisAlignItems = "CENTER";
  component.counterAxisAlignItems = "CENTER";

  // Create SVG node from the loader SVG (parsed from loader.tsx)
  const svgString = loaderData.svgString;

  try {
    const svgNode = figma.createNodeFromSvg(svgString);
    svgNode.name = "Spinner";

    // Resize to match the component size
    svgNode.resize(sizeValue, sizeValue);

    // Set constraints to SCALE for proper resizing
    svgNode.constraints = {
      horizontal: "SCALE",
      vertical: "SCALE",
    };

    // Bind stroke colors to text-color-surface
    const strokeVar = getVariableByName(VAR_NAMES.text.default);
    if (strokeVar) {
      // Traverse all children and bind stroke colors
      traverseAndBindStrokes(svgNode, strokeVar.id);
    }

    component.appendChild(svgNode);
  } catch (error) {
    console.error("Failed to create SVG node for Loader:", error);
    // Fallback: create a simple ellipse
    const fallbackCircle = figma.createEllipse();
    fallbackCircle.name = "Fallback";
    fallbackCircle.resize(sizeValue - 4, sizeValue - 4);
    fallbackCircle.x = 2;
    fallbackCircle.y = 2;
    fallbackCircle.fills = [];
    fallbackCircle.strokes = [{ type: "SOLID", color: COLORS.placeholder }];
    fallbackCircle.strokeWeight = 2;
    component.appendChild(fallbackCircle);
  }

  return component;
}

/**
 * Traverse SVG node tree and bind stroke colors to a variable
 */
function traverseAndBindStrokes(node: SceneNode, variableId: string): void {
  // Check if this node type can have strokes
  if (
    node.type === "VECTOR" ||
    node.type === "ELLIPSE" ||
    node.type === "RECTANGLE" ||
    node.type === "LINE" ||
    node.type === "POLYGON" ||
    node.type === "STAR"
  ) {
    const strokeNode = node as
      | VectorNode
      | EllipseNode
      | RectangleNode
      | LineNode
      | PolygonNode
      | StarNode;
    if (strokeNode.strokes && strokeNode.strokes.length > 0) {
      const strokeWeight =
        typeof strokeNode.strokeWeight === "number"
          ? strokeNode.strokeWeight
          : 1;
      bindStrokeToVariable(node, variableId, strokeWeight);
    }
  }

  // Recursively process children for frames and groups
  if (node.type === "FRAME" || node.type === "GROUP") {
    const containerNode = node as FrameNode | GroupNode;
    for (let i = 0; i < containerNode.children.length; i++) {
      traverseAndBindStrokes(containerNode.children[i], variableId);
    }
  }
}

// ============================================================
// TESTABLE EXPORTS - Pure functions for testing (no Figma API calls)
// ============================================================

/**
 * Get Loader size configuration from loader-data.json
 *
 * Returns the size values and their metadata.
 *
 * @returns Object with size values array and size configs
 */
export function getLoaderSizeConfig() {
  return {
    values: Object.keys(loaderData.sizes),
    sizes: loaderData.sizes,
  };
}

/**
 * Get SVG data from loader-data.json
 *
 * Returns the SVG string and viewBox dimensions.
 *
 * @returns Object with SVG string, viewBox, and dimensions
 */
export function getLoaderSvgData() {
  return {
    svgString: loaderData.svgString,
    viewBox: loaderData.viewBox,
    width: loaderData.width,
    height: loaderData.height,
    circles: loaderData.circles,
  };
}

/**
 * Get computed dimensions for a specific size
 *
 * @param size - Size key (sm, base, lg)
 * @returns Object with dimension data for the size
 */
export function getLoaderSizeDimensions(size: string) {
  const sizeConfig = loaderData.sizes[size as keyof typeof loaderData.sizes];
  if (!sizeConfig) {
    throw new Error("Invalid size: " + size);
  }

  return {
    size,
    value: sizeConfig.value,
    description: sizeConfig.description,
  };
}

/**
 * Get color binding for Loader
 *
 * Returns the semantic token used for stroke color.
 *
 * @returns Object with color token name
 */
export function getLoaderColorBinding() {
  return {
    strokeVariable: VAR_NAMES.text.default,
  };
}

/**
 * Get all Loader data for snapshot testing
 *
 * Returns complete intermediate data structure that the generator
 * computes before making Figma API calls.
 *
 * @returns Complete Loader data structure
 */
export function getAllLoaderData() {
  const sizeConfig = getLoaderSizeConfig();
  const svgData = getLoaderSvgData();
  const colorBinding = getLoaderColorBinding();

  return {
    sizeConfig,
    svgData,
    colorBinding,
    sizes: sizeConfig.values.map((size) => {
      return getLoaderSizeDimensions(size);
    }),
  };
}

/**
 * Generate Loader ComponentSet
 *
 * Creates a "Loader" ComponentSet with size variants.
 * Creates both light and dark mode sections.
 *
 * @param page - The page to add components to
 * @param startY - Y position to start placing the section
 * @returns The Y position after this section (for next section placement)
 */
export async function generateLoaderComponents(
  page: PageNode,
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  logStart("Loader", "Y=" + startY);

  try {
    figma.currentPage = page;

    const sizes = Object.keys(loaderData.sizes);
    const components: ComponentNode[] = [];
    const rowLabels: { y: number; text: string }[] = [];

    const labelColumnWidth = 120;
    const componentGap = 24;
    let currentY = 0;

    // Create components for each size
    for (let i = 0; i < sizes.length; i++) {
      const size = sizes[i];
      const sizeValue =
        loaderData.sizes[size as keyof typeof loaderData.sizes].value;

      logProgress("Loader", "Creating size=" + size);
      const component = await createLoaderComponent(size);
      component.x = labelColumnWidth;
      component.y = currentY;

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

      currentY += sizeValue + componentGap;
    }

    logProgress("Loader", "Combining as variants...");
    // @ts-ignore - combineAsVariants works at runtime
    const componentSet = figma.combineAsVariants(components, page);
    componentSet.name = "Loader";
    componentSet.description =
      "Loader - Circular loading spinner. Sizes: sm (16px), base (24px), lg (32px). " +
      "Uses currentColor for stroke, so set text color on parent to change color. " +
      "Static representation of animated spinner from loader.tsx.";
    componentSet.layoutMode = "NONE";

    // Calculate content dimensions
    const contentWidth = componentSet.width + labelColumnWidth;
    const contentHeight = componentSet.height;

    // Add contentYOffset for title space inside frame
    const contentYOffset = SECTION_TITLE.height;

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

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

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

    // Add section titles inside frames

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

    // Create instances for dark section
    for (let k = 0; k < components.length; k++) {
      const origComp = components[k];
      const instance = origComp.createInstance();
      // Position relative to componentSet position, not origComp position
      instance.x = SECTION_PADDING + labelColumnWidth;
      instance.y = origComp.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 +
          GRID_LAYOUT.labelVerticalOffset.sm,
      );
      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 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 Loader ComponentSet with " +
        sizes.length +
        " variants (light + dark)",
    );

    return startY + totalHeight + SECTION_GAP;
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const errorStack = error instanceof Error ? error.stack : "";
    console.error("Loader generation failed: " + errorMessage);
    console.error("Stack: " + errorStack);
    throw error;
  }
}
