import type { PenDocument, PenNode } from '@/types/pen';
import { getAllChildren } from '@/stores/document-tree-utils';
export { deepCloneNode } from '@/stores/document-tree-utils';
import type { ComponentCategory, KitComponent } from '@/types/uikit';

/**
 * Walk the document tree and extract all reusable nodes as KitComponent metadata.
 */
export function extractComponentsFromDocument(
  doc: PenDocument,
  metaOverrides?: Record<string, { category: ComponentCategory; tags: string[] }>,
): KitComponent[] {
  const components: KitComponent[] = [];
  walkTree(getAllChildren(doc), (node) => {
    if ('reusable' in node && node.reusable) {
      const w = 'width' in node && typeof node.width === 'number' ? node.width : 100;
      const h = 'height' in node && typeof node.height === 'number' ? node.height : 100;
      const meta = metaOverrides?.[node.id];
      components.push({
        id: node.id,
        name: node.name ?? node.id,
        category: meta?.category ?? inferCategory(node.name ?? ''),
        tags: meta?.tags ?? inferTags(node.name ?? ''),
        width: w,
        height: h,
      });
    }
  });
  return components;
}

/**
 * Find a specific node in a document tree by ID.
 */
export function findReusableNode(doc: PenDocument, nodeId: string): PenNode | undefined {
  return findInTree(getAllChildren(doc), nodeId);
}

/**
 * Recursively collect all $variable references used by a node tree.
 */
export function collectVariableRefs(node: PenNode): Set<string> {
  const refs = new Set<string>();
  collectRefs(node, refs);
  return refs;
}

// deepCloneNode re-exported from pen-core via document-tree-utils

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

function walkTree(nodes: PenNode[], visitor: (node: PenNode) => void): void {
  for (const node of nodes) {
    visitor(node);
    if ('children' in node && node.children) {
      walkTree(node.children, visitor);
    }
  }
}

function findInTree(nodes: PenNode[], id: string): PenNode | undefined {
  for (const node of nodes) {
    if (node.id === id) return node;
    if ('children' in node && node.children) {
      const found = findInTree(node.children, id);
      if (found) return found;
    }
  }
  return undefined;
}

function isVariableRef(value: unknown): value is string {
  return typeof value === 'string' && value.startsWith('$');
}

function collectRefs(node: PenNode, refs: Set<string>): void {
  if (isVariableRef(node.opacity)) refs.add(node.opacity);
  if (isVariableRef(node.enabled)) refs.add(node.enabled);

  if ('gap' in node && isVariableRef(node.gap)) refs.add(node.gap as string);
  if ('padding' in node && isVariableRef(node.padding)) refs.add(node.padding as string);

  if ('fill' in node && Array.isArray(node.fill)) {
    for (const f of node.fill) {
      if (f.type === 'solid' && isVariableRef(f.color)) refs.add(f.color);
    }
  }

  if ('stroke' in node && node.stroke) {
    const s = node.stroke;
    if (Array.isArray(s.fill)) {
      for (const f of s.fill) {
        if (f.type === 'solid' && isVariableRef(f.color)) refs.add(f.color);
      }
    }
    if (isVariableRef(s.thickness)) refs.add(s.thickness as unknown as string);
  }

  if ('children' in node && node.children) {
    for (const child of node.children) {
      collectRefs(child, refs);
    }
  }
}

const CATEGORY_KEYWORDS: Record<ComponentCategory, string[]> = {
  buttons: ['button', 'btn', 'cta'],
  inputs: [
    'input',
    'text field',
    'textarea',
    'checkbox',
    'radio',
    'toggle',
    'switch',
    'select',
    'form',
  ],
  cards: ['card', 'tile'],
  navigation: ['nav', 'navbar', 'tab', 'breadcrumb', 'menu', 'sidebar'],
  layout: ['divider', 'separator', 'spacer', 'container', 'grid'],
  feedback: ['alert', 'banner', 'toast', 'badge', 'tag', 'chip', 'avatar', 'tooltip'],
  'data-display': ['table', 'list', 'stat', 'chart', 'progress'],
  other: [],
};

function inferCategory(name: string): ComponentCategory {
  const lower = name.toLowerCase();
  for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {
    if (category === 'other') continue;
    for (const kw of keywords) {
      if (lower.includes(kw)) return category as ComponentCategory;
    }
  }
  return 'other';
}

function inferTags(name: string): string[] {
  return name
    .toLowerCase()
    .replace(/[^a-z0-9\s-]/g, '')
    .split(/[\s-]+/)
    .filter(Boolean);
}
