/**
 * Switch Component Generator
 *
 * Generates high-quality Switch components in Figma matching the code implementation.
 *
 * ## Generated Components
 *
 * ### 1. Switch (ComponentSet)
 * Single switch with label. Properties:
 * - size: sm, base, lg
 * - checked: false, true
 * - variant: default, error
 * - disabled: false, true
 * - controlFirst: true (switch→label), false (label→switch)
 *
 * ### 2. Switch.Group (ComponentSet)
 * Fieldset containing multiple switches. Properties:
 * - hasDescription: false, true
 * - hasError: false, true
 * - controlFirst: true, false
 *
 * Component Structure (from switch.tsx):
 * - Single Switch: Field wrapper with switch + label
 * - Switch.Group: Fieldset with legend, optional description/error, Switch.Item children
 * - Switch track: rounded-full pill, bg-kumo-recessed (off) / bg-kumo-brand or bg-kumo-danger (on)
 * - Switch thumb: circular, bg-white, translates right when checked
 *
 * @see packages/kumo/src/components/switch/switch.tsx
 */

import {
  bindFillToVariable,
  getVariableByName,
  createModeSection,
  createTextNode,
  createRowLabel,
  createColumnHeaders,
  bindTextColorToVariable,
  BORDER_RADIUS,
  FONT_SIZE,
  SECTION_PADDING,
  SECTION_GAP,
  GRID_LAYOUT,
  SECTION_LAYOUT,
  OPACITY,
  SPACING,
  VAR_NAMES,
} from "./shared";
import themeData from "../generated/theme-data.json";
import { logInfo } from "../logger";

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

// Extract props from registry
const switchProps = registry.components.Switch.props;

const variantProp = switchProps.variant as {
  values: string[];
  classes: Record<string, string>;
  descriptions: Record<string, string>;
  default: string;
};

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

/**
 * Parse switch dimensions from registry size classes
 * Size classes: h-5.5 w-8.5 (sm), h-6.5 w-10.5 (base), h-7.5 w-12.5 (lg)
 * Height includes the padding (p-1 = 4px = 2px * 2)
 * Thumb is "aspect-square h-full" (height of container minus padding)
 *
 * NOTE: Parser doesn't support width property or fractional Tailwind classes,
 * so we parse dimensions manually using regex and 1 Tailwind unit = 4px conversion
 */
function parseSwitchDimensions(size: string): {
  width: number;
  height: number;
} {
  const sizeClasses = sizeProp.classes[size] || "";

  // Parse fractional Tailwind classes manually
  // Classes format: "h-5.5 w-8.5" or "h-6.5 w-10.5" or "h-7.5 w-12.5"
  const heightMatch = sizeClasses.match(/h-(\d+(?:\.\d+)?)/);
  const widthMatch = sizeClasses.match(/w-(\d+(?:\.\d+)?)/);

  if (heightMatch && widthMatch) {
    const heightUnits = parseFloat(heightMatch[1]);
    const widthUnits = parseFloat(widthMatch[1]);
    return {
      width: widthUnits * 4,
      height: heightUnits * 4,
    };
  }

  // COMPONENT-SPECIFIC: Fallback dimensions for Switch component sizes
  // These values are derived from Tailwind fractional classes in switch.tsx:
  // sm: h-5.5 w-8.5 (5.5*4=22px, 8.5*4=34px)
  // base: h-6.5 w-10.5 (6.5*4=26px, 10.5*4=42px)
  // lg: h-7.5 w-12.5 (7.5*4=30px, 12.5*4=50px)
  //
  // FALLBACK ONLY: These hardcoded values are only used if regex parsing of
  // sizeProp.classes fails. The primary source is the registry classes which
  // are parsed dynamically. If you see drift here, update the registry or
  // check that parseSwitchDimensions regex is working correctly.
  const fallbackDimensions: Record<string, { width: number; height: number }> =
    {
      sm: { width: 34, height: 22 }, // h-5.5 w-8.5 = 5.5*4, 8.5*4
      base: { width: 42, height: 26 }, // h-6.5 w-10.5 = 6.5*4, 10.5*4
      lg: { width: 50, height: 30 }, // h-7.5 w-12.5 = 7.5*4, 12.5*4
    };
  return fallbackDimensions[size] || fallbackDimensions.base;
}

/**
 * Switch dimensions computed from registry
 * Dynamically generated from sizeProp.classes
 */
const SWITCH_DIMENSIONS: Record<string, { width: number; height: number }> = {};
for (let i = 0; i < sizeProp.values.length; i++) {
  const sizeValue = sizeProp.values[i];
  SWITCH_DIMENSIONS[sizeValue] = parseSwitchDimensions(sizeValue);
}

/**
 * Thumb padding from switch.tsx (p-1 = 4px)
 * Derived from theme-data.json to prevent drift
 */
const SWITCH_PADDING = themeData.tailwind.spacing.scale["1"]; // p-1 = 4px

/**
 * Gap between switch and label (gap-2 = 8px)
 * Derived from theme-data.json to prevent drift
 */
const SWITCH_LABEL_GAP = SPACING.base; // gap-2 = 8px

/**
 * Create the switch thumb (the circular moving part)
 */
function createSwitchThumb(size: string, checked: boolean): EllipseNode {
  const dimensions = SWITCH_DIMENSIONS[size];
  const thumbSize = dimensions.height - SWITCH_PADDING * 2;

  const thumb = figma.createEllipse();
  thumb.name = "Thumb";
  thumb.resize(thumbSize, thumbSize);

  // Background: white (not a variable, hardcoded in switch.tsx)
  thumb.fills = [
    {
      type: "SOLID",
      color: { r: 1, g: 1, b: 1 },
    },
  ];

  // Position: translate-x-full when checked
  if (checked) {
    thumb.x = dimensions.width - thumbSize - SWITCH_PADDING;
  } else {
    thumb.x = SWITCH_PADDING;
  }
  thumb.y = SWITCH_PADDING;

  return thumb;
}

/**
 * Create the switch track (the pill-shaped background)
 */
function createSwitchTrack(
  size: string,
  variant: string,
  checked: boolean,
  disabled: boolean,
): FrameNode {
  const dimensions = SWITCH_DIMENSIONS[size];

  const track = figma.createFrame();
  track.name = "Switch Track";
  track.resize(dimensions.width, dimensions.height);

  // No auto-layout for track - thumb positioned absolutely
  track.layoutMode = "NONE";

  // Border radius: rounded-full
  track.cornerRadius = BORDER_RADIUS.full;

  // Background fill based on state
  // - Unchecked: bg-kumo-recessed
  // - Checked + default: bg-kumo-brand
  // - Checked + error: bg-kumo-danger
  let bgVariableName: string = VAR_NAMES.color.recessed;
  if (checked && !disabled) {
    if (variant === "error") {
      bgVariableName = VAR_NAMES.color.danger;
    } else {
      bgVariableName = VAR_NAMES.color.brand;
    }
  }

  const bgVar = getVariableByName(bgVariableName);
  if (bgVar) {
    bindFillToVariable(track, bgVar.id);
  }

  // Border: border-transparent (no visible border in default state)
  // Note: The switch uses border-transparent, so we skip adding a stroke

  // Add the thumb
  const thumb = createSwitchThumb(size, checked);
  track.appendChild(thumb);

  return track;
}

/**
 * Create a single switch component with label (controlFirst=true, for backwards compat)
 */
async function createSwitchComponent(
  size: string,
  variant: string,
  checked: boolean,
  disabled: boolean,
  labelText: string,
): Promise<ComponentNode> {
  // Delegate to createSwitchWithLayout with controlFirst=true
  return createSwitchWithLayout(
    size,
    variant,
    checked,
    disabled,
    true,
    labelText,
  );
}

/**
 * Get a simple label for the switch
 */
function getSwitchLabel(): string {
  return "Label";
}

/**
 * Create a single switch component with label and controlFirst option
 */
async function createSwitchWithLayout(
  size: string,
  variant: string,
  checked: boolean,
  disabled: boolean,
  controlFirst: boolean,
  labelText: string,
): Promise<ComponentNode> {
  const component = figma.createComponent();
  component.name =
    "size=" +
    size +
    ", checked=" +
    checked +
    ", variant=" +
    variant +
    ", disabled=" +
    disabled +
    ", controlFirst=" +
    controlFirst;

  const variantDesc = variantProp.descriptions[variant] || "";
  const sizeDesc = sizeProp.descriptions[size] || "";
  const layoutDesc = controlFirst
    ? "Switch appears before label"
    : "Label appears before switch";
  component.description = variantDesc + " - " + sizeDesc + ". " + layoutDesc;

  // Configure as horizontal auto-layout (switch + label or label + switch)
  component.layoutMode = "HORIZONTAL";
  component.primaryAxisAlignItems = "MIN";
  component.counterAxisAlignItems = "CENTER";
  component.primaryAxisSizingMode = "AUTO";
  component.counterAxisSizingMode = "AUTO";
  component.itemSpacing = SWITCH_LABEL_GAP;
  component.fills = [];

  // Create switch track
  const switchTrack = createSwitchTrack(size, variant, checked, disabled);

  // Create label text
  const label = await createTextNode(labelText, FONT_SIZE.base, 500);
  const textVar = getVariableByName(VAR_NAMES.text.default);
  if (textVar) {
    bindTextColorToVariable(label, textVar.id);
  }

  // Add in order based on controlFirst
  if (controlFirst) {
    component.appendChild(switchTrack);
    component.appendChild(label);
  } else {
    component.appendChild(label);
    component.appendChild(switchTrack);
  }

  // Apply disabled state: opacity-50
  if (disabled) {
    component.opacity = OPACITY.disabled;
  }

  return component;
}

/**
 * Create a Switch.Item (switch within a group, no Field wrapper)
 */
async function createSwitchItem(
  checked: boolean,
  disabled: boolean,
  controlFirst: boolean,
  labelText: string,
): Promise<FrameNode> {
  const item = figma.createFrame();
  item.name = "Switch.Item";
  item.layoutMode = "HORIZONTAL";
  item.primaryAxisAlignItems = "MIN";
  item.counterAxisAlignItems = "CENTER";
  item.primaryAxisSizingMode = "AUTO";
  item.counterAxisSizingMode = "AUTO";
  item.itemSpacing = SWITCH_LABEL_GAP;
  item.fills = [];

  // Create switch track (always base size in groups)
  const switchTrack = createSwitchTrack("base", "default", checked, disabled);

  // Create label text
  const label = await createTextNode(labelText, FONT_SIZE.base, 500);
  const textVar = getVariableByName(VAR_NAMES.text.default);
  if (textVar) {
    bindTextColorToVariable(label, textVar.id);
  }

  // Add in order based on controlFirst
  if (controlFirst) {
    item.appendChild(switchTrack);
    item.appendChild(label);
  } else {
    item.appendChild(label);
    item.appendChild(switchTrack);
  }

  // Apply disabled state
  if (disabled) {
    item.opacity = OPACITY.disabled;
  }

  return item;
}

/**
 * Create a Switch.Group component
 */
async function createSwitchGroupComponent(
  hasDescription: boolean,
  hasError: boolean,
  controlFirst: boolean,
): Promise<ComponentNode> {
  const component = figma.createComponent();
  component.name =
    "hasDescription=" +
    hasDescription +
    ", hasError=" +
    hasError +
    ", controlFirst=" +
    controlFirst;

  component.description =
    "Switch group with fieldset and legend. " +
    (hasDescription ? "Includes description text. " : "") +
    (hasError ? "Shows error message. " : "") +
    (controlFirst
      ? "Switches appear before labels."
      : "Labels appear before switches.");

  // Fieldset container: flex flex-col gap-4 rounded-lg border border-kumo-line p-4
  component.layoutMode = "VERTICAL";
  component.primaryAxisSizingMode = "AUTO";
  component.counterAxisSizingMode = "AUTO";
  component.itemSpacing = themeData.tailwind.spacing.scale["4"]; // gap-4 = 16px
  component.paddingLeft = themeData.tailwind.spacing.scale["4"]; // p-4 = 16px
  component.paddingRight = themeData.tailwind.spacing.scale["4"];
  component.paddingTop = themeData.tailwind.spacing.scale["4"];
  component.paddingBottom = themeData.tailwind.spacing.scale["4"];
  component.cornerRadius = BORDER_RADIUS.lg; // rounded-lg = 8px

  // Border: border-kumo-line
  const borderVar = getVariableByName(VAR_NAMES.color.line);
  if (borderVar) {
    component.strokes = [
      figma.variables.setBoundVariableForPaint(
        { type: "SOLID", color: { r: 0, g: 0, b: 0 } },
        "color",
        borderVar,
      ),
    ];
    component.strokeWeight = 1;
  }

  // Background: transparent (no fill)
  component.fills = [];

  // Legend: text-lg font-medium text-kumo-default
  const legend = await createTextNode(
    "Notification settings",
    FONT_SIZE.lg,
    500,
  );
  const textVar = getVariableByName(VAR_NAMES.text.default);
  if (textVar) {
    bindTextColorToVariable(legend, textVar.id);
  }
  component.appendChild(legend);

  // Items container: flex flex-col gap-2
  const itemsContainer = figma.createFrame();
  itemsContainer.name = "Items";
  itemsContainer.layoutMode = "VERTICAL";
  itemsContainer.primaryAxisSizingMode = "AUTO";
  itemsContainer.counterAxisSizingMode = "AUTO";
  itemsContainer.itemSpacing = SPACING.base; // gap-2 = 8px
  itemsContainer.fills = [];

  // Add 3 switch items
  const item1 = await createSwitchItem(
    true,
    false,
    controlFirst,
    "Email notifications",
  );
  const item2 = await createSwitchItem(
    false,
    false,
    controlFirst,
    "SMS notifications",
  );
  const item3 = await createSwitchItem(
    true,
    false,
    controlFirst,
    "Push notifications",
  );

  itemsContainer.appendChild(item1);
  itemsContainer.appendChild(item2);
  itemsContainer.appendChild(item3);

  component.appendChild(itemsContainer);

  // Error message: text-sm text-kumo-danger (14px, but we use xs=12 as closest)
  if (hasError) {
    const errorText = await createTextNode(
      "You must enable at least one notification method",
      FONT_SIZE.xs,
      400,
    );
    const errorVar = getVariableByName(VAR_NAMES.text.danger);
    if (errorVar) {
      bindTextColorToVariable(errorText, errorVar.id);
    }
    component.appendChild(errorText);
  }

  // Description: text-sm text-kumo-subtle (14px, but we use xs=12 as closest)
  if (hasDescription) {
    const descText = await createTextNode(
      "Choose how you want to be notified about important updates",
      FONT_SIZE.xs,
      400,
    );
    const mutedVar = getVariableByName(VAR_NAMES.text.subtle);
    if (mutedVar) {
      bindTextColorToVariable(descText, mutedVar.id);
    }
    component.appendChild(descText);
  }

  return component;
}

/**
 * Generate Switch ComponentSet
 *
 * Layout:
 * - Column headers: size=sm | size=base | size=lg
 * - Row 1: checked=false, variant=default
 * - Row 2: checked=true, variant=default
 * - Row 3: checked=false, variant=default, disabled=true
 * - Row 4: checked=true, variant=default, disabled=true
 * - Row 5: checked=false, variant=error
 * - Row 6: checked=true, variant=error
 * - Row 7: checked=false, variant=error, disabled=true
 * - Row 8: checked=true, variant=error, disabled=true
 *
 * @param page - Target page for components
 * @param startY - Y position to start placing sections
 * @returns The Y position after all sections
 */
export async function generateSwitchComponents(
  page: PageNode,
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  figma.currentPage = page;

  const sizes: string[] = sizeProp.values;

  // Generate all meaningful combinations
  const components: ComponentNode[] = [];

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

  // Layout grid spacing
  const componentGap = 24;
  const rowGap = GRID_LAYOUT.rowGap.standard;
  const headerRowHeight = GRID_LAYOUT.headerRowHeight;

  const labelColumnWidth = GRID_LAYOUT.labelColumnWidth.widest;

  // Column headers for sizes
  const columnHeaderTexts = ["size=sm", "size=base", "size=lg"];

  // Track layout - each scenario on its own row for readability
  let currentY = headerRowHeight; // Start below header row

  // Row 1: checked=false, variant=default
  rowLabels.push({ y: currentY, text: "checked=false, variant=default" });
  let currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "default",
      false,
      false,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 2: checked=true, variant=default
  rowLabels.push({ y: currentY, text: "checked=true, variant=default" });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "default",
      true,
      false,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 3: checked=false, variant=default, disabled=true
  rowLabels.push({
    y: currentY,
    text: "checked=false, variant=default, disabled=true",
  });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "default",
      false,
      true,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 4: checked=true, variant=default, disabled=true
  rowLabels.push({
    y: currentY,
    text: "checked=true, variant=default, disabled=true",
  });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "default",
      true,
      true,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 5: checked=false, variant=error
  rowLabels.push({ y: currentY, text: "checked=false, variant=error" });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "error",
      false,
      false,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 6: checked=true, variant=error
  rowLabels.push({ y: currentY, text: "checked=true, variant=error" });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "error",
      true,
      false,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 7: checked=false, variant=error, disabled=true
  rowLabels.push({
    y: currentY,
    text: "checked=false, variant=error, disabled=true",
  });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "error",
      false,
      true,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }
  currentY = currentY + rowGap;

  // Row 8: checked=true, variant=error, disabled=true
  rowLabels.push({
    y: currentY,
    text: "checked=true, variant=error, disabled=true",
  });
  currentX = labelColumnWidth;
  for (let i = 0; i < sizes.length; i++) {
    const component = await createSwitchComponent(
      sizes[i],
      "error",
      true,
      true,
      getSwitchLabel(),
    );
    component.x = currentX;
    component.y = currentY;
    currentX = currentX + component.width + componentGap;
    components.push(component);
  }

  // Combine into ComponentSet
  // @ts-ignore - combineAsVariants works at runtime
  const componentSet = figma.combineAsVariants(components, page);
  componentSet.name = "Switch";
  componentSet.description =
    "Switch component with size (sm/base/lg), checked (false/true), variant (default/error), and disabled properties. Includes label text.";

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

  // Create dark mode section
  const darkSection = createModeSection(page, "Switch", "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;

  // Build column headers with positions from first row components
  let columnHeaders: { x: number; text: string }[] = [];
  // Get first 3 components (first row) for column positions
  for (let i = 0; i < Math.min(3, components.length); i++) {
    columnHeaders.push({
      x: components[i].x + SECTION_PADDING,
      text: columnHeaderTexts[i],
    });
  }

  // Add column headers to light section
  await createColumnHeaders(columnHeaders, SECTION_PADDING, lightSection.frame);

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

  // Create instances for dark section
  // Note: component.x/y are relative to ComponentSet after combineAsVariants
  // We need to add labelColumnWidth to match the light section layout
  for (let i = 0; i < components.length; i++) {
    const component = components[i];
    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, SECTION_PADDING, darkSection.frame);

  // Add row labels to dark section
  for (let i = 0; i < rowLabels.length; i++) {
    const label = rowLabels[i];
    const labelNode = await createRowLabel(
      label.text,
      SECTION_PADDING,
      SECTION_PADDING + label.y + GRID_LAYOUT.labelVerticalOffset.sm,
    );
    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;

  logInfo(
    "✅ Generated Switch ComponentSet with " +
      components.length +
      " variants (light + dark)",
  );

  return startY + totalHeight + SECTION_GAP;
}

/**
 * Generate Switch.Group ComponentSet
 *
 * Creates variants for:
 * - hasDescription: false, true
 * - hasError: false, true
 * - controlFirst: true, false
 *
 * Total: 2 × 2 × 2 = 8 variants
 *
 * @param page - Target page for components
 * @param startY - Y position to start placing sections
 * @returns The Y position after all sections
 */
export async function generateSwitchGroupComponents(
  page: PageNode,
  startY: number,
): Promise<number> {
  if (startY === undefined) startY = 100;

  logInfo("Switch.Group: Starting generation at Y=" + startY);

  figma.currentPage = page;

  const components: ComponentNode[] = [];

  // Generate all combinations: hasDescription × hasError × controlFirst
  const hasDescriptionValues = [false, true];
  const hasErrorValues = [false, true];
  const controlFirstValues = [true, false];

  for (let d = 0; d < hasDescriptionValues.length; d++) {
    for (let e = 0; e < hasErrorValues.length; e++) {
      for (let c = 0; c < controlFirstValues.length; c++) {
        const component = await createSwitchGroupComponent(
          hasDescriptionValues[d],
          hasErrorValues[e],
          controlFirstValues[c],
        );
        components.push(component);
      }
    }
  }

  // Combine into ComponentSet
  // @ts-ignore - combineAsVariants works at runtime
  const componentSet = figma.combineAsVariants(components, page);
  componentSet.name = "Switch.Group";
  componentSet.description =
    "Switch group with fieldset, legend, optional description and error message. " +
    "Use for multiple related switches. Properties: hasDescription, hasError, controlFirst.";

  // Auto-layout the component set for clean display
  componentSet.layoutMode = "HORIZONTAL";
  componentSet.layoutWrap = "WRAP";
  componentSet.itemSpacing = 24;
  componentSet.counterAxisSpacing = 24;
  componentSet.primaryAxisSizingMode = "AUTO";
  componentSet.counterAxisSizingMode = "AUTO";

  // Create light mode section
  const lightSection = createModeSection(page, "Switch.Group", "light");

  // Create dark mode section
  const darkSection = createModeSection(page, "Switch.Group", "dark");

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

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

  // Resize sections to fit content
  const contentWidth = componentSet.width + SECTION_PADDING * 2;
  const contentHeight = componentSet.height + SECTION_PADDING * 2;

  lightSection.frame.resize(contentWidth, contentHeight);
  darkSection.frame.resize(contentWidth, contentHeight);

  // 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 + contentWidth + SECTION_LAYOUT.modeGap;
  darkSection.frame.y = startY;

  logInfo(
    "✅ Generated Switch.Group ComponentSet with " +
      components.length +
      " variants (light + dark)",
  );

  return startY + contentHeight + SECTION_GAP;
}

/**
 * Testable exports (pure functions for testing - no Figma API calls)
 */

/**
 * Get variant configuration from registry
 */
export function getSwitchVariantConfig() {
  return {
    values: variantProp.values,
    classes: variantProp.classes,
    descriptions: variantProp.descriptions,
    default: variantProp.default,
  };
}

/**
 * Get size configuration from registry
 */
export function getSwitchSizeConfig() {
  return {
    values: sizeProp.values,
    classes: sizeProp.classes,
    descriptions: sizeProp.descriptions,
    default: sizeProp.default,
  };
}

/**
 * Get switch dimensions for a specific size
 */
export function getSwitchDimensions(size: string) {
  const dimensions = SWITCH_DIMENSIONS[size];
  if (!dimensions) {
    throw new Error("Invalid size: " + size);
  }
  return {
    size: size,
    width: dimensions.width,
    height: dimensions.height,
    thumbSize: dimensions.height - SWITCH_PADDING * 2,
    padding: SWITCH_PADDING,
  };
}

/**
 * Get switch track color binding for a specific state
 */
export function getSwitchTrackColorBinding(
  variant: string,
  checked: boolean,
  disabled: boolean,
) {
  let bgVariableName: string = VAR_NAMES.color.recessed;
  if (checked && !disabled) {
    if (variant === "error") {
      bgVariableName = VAR_NAMES.color.danger;
    } else {
      bgVariableName = VAR_NAMES.color.brand;
    }
  }
  return {
    variant: variant,
    checked: checked,
    disabled: disabled,
    bgVariableName: bgVariableName,
  };
}

/**
 * Get complete switch data for all variants (golden path)
 */
export function getAllSwitchVariantData() {
  const variantConfig = getSwitchVariantConfig();
  const sizeConfig = getSwitchSizeConfig();

  const allDimensions = sizeConfig.values.map(function (size) {
    return getSwitchDimensions(size);
  });

  const allTrackColors: {
    variant: string;
    checked: boolean;
    disabled: boolean;
    bgVariableName: string;
  }[] = [];

  // Generate all meaningful combinations
  const variantValues = variantConfig.values;
  const checkedValues = [false, true];
  const disabledValues = [false, true];

  for (let v = 0; v < variantValues.length; v++) {
    for (let c = 0; c < checkedValues.length; c++) {
      for (let d = 0; d < disabledValues.length; d++) {
        allTrackColors.push(
          getSwitchTrackColorBinding(
            variantValues[v],
            checkedValues[c],
            disabledValues[d],
          ),
        );
      }
    }
  }

  return {
    variantConfig: variantConfig,
    sizeConfig: sizeConfig,
    dimensions: allDimensions,
    trackColors: allTrackColors,
    labelGap: SWITCH_LABEL_GAP,
    thumbColor: { r: 1, g: 1, b: 1 }, // white
    disabledOpacity: OPACITY.disabled, // 0.5 from shared.ts
  };
}

/**
 * Legacy exports for backwards compatibility
 */
export const SWITCH_VARIANTS_EXPORT = variantProp.values;
export const SWITCH_SIZES_EXPORT = sizeProp.values;
