export interface CssToken {
  /** `--fox-` 를 뗀 이름 */
  name: string;
  property: string;
  /** 그 모드에서 선언된 원문. 참조면 `var(--fox-…)`, 아니면 값. */
  declaration: string;
  /** 참조면 가리키는 토큰 이름, 아니면 null */
  alias: string | null;
}

export interface CssSection {
  id: string;
  label: string;
  category: string | null;
  /** 이 섹션의 값이 어디서 왔는지 */
  source: string;
  tokens: CssToken[];
}

const PREFIX = "--fox-";

/** `var(--fox-x)` 형태면 가리키는 토큰 이름을 돌려준다. */
function aliasOf(value: string): string | null {
  const match = /^var\(\s*--fox-([a-z0-9-]+)\s*\)$/.exec(value.trim());
  return match ? match[1] : null;
}

/** 내용에 `var(--fox-…)`가 중첩돼 정규식으로는 못 자르므로 괄호 짝을 센다. */
function extractFallback(text: string, marker: string): string | null {
  const start = text.indexOf(marker);
  if (start < 0) {
    return null;
  }

  let depth = 1;
  let i = start + marker.length;
  for (; i < text.length && depth > 0; i += 1) {
    if (text[i] === "(") depth += 1;
    else if (text[i] === ")") depth -= 1;
  }
  return text.slice(start + marker.length, i - 1).trim();
}

/** 괄호 안의 콤마는 무시하고 최상위 콤마로 나눈다. */
function splitTopLevel(text: string): [string, string] | null {
  let depth = 0;
  for (let i = 0; i < text.length; i += 1) {
    const ch = text[i];
    if (ch === "(") depth += 1;
    else if (ch === ")") depth -= 1;
    else if (ch === "," && depth === 0) {
      return [text.slice(0, i).trim(), text.slice(i + 1).trim()];
    }
  }
  return null;
}

/**
 * Lightning CSS가 `light-dark()`를 폴리필한 형태를 먼저 보고, 폴리필이 없는 설정을
 * 대비해 원형도 처리한다.
 */
function splitLightDark(declaration: string): { light: string; dark: string } | null {
  const light = extractFallback(declaration, "var(--lightningcss-light,");
  const dark = extractFallback(declaration, "var(--lightningcss-dark,");
  if (light !== null && dark !== null) {
    return { light, dark };
  }

  if (declaration.startsWith("light-dark(")) {
    const inner = extractFallback(declaration, "light-dark(");
    const pair = inner === null ? null : splitTopLevel(inner);
    if (pair) {
      return { light: pair[0], dark: pair[1] };
    }
  }

  return null;
}

function toToken(name: string, declaration: string): CssToken {
  return {
    name,
    property: `${PREFIX}${name}`,
    declaration,
    alias: aliasOf(declaration),
  };
}

interface RawTokens {
  /** `:root` 기본 선언 — 색상은 light-dark 형태, 반응형은 모바일 값 */
  base: Map<string, string>;
  /** `@media (min-width: …)` 안의 오버라이드 — PC 값 */
  pc: Map<string, string>;
}

function readRaw(): RawTokens {
  const base = new Map<string, string>();
  const pc = new Map<string, string>();

  const collect = (rule: CSSStyleRule, into: Map<string, string>) => {
    if (rule.selectorText !== ":root") {
      return;
    }
    for (const property of Array.from(rule.style)) {
      if (property.startsWith(PREFIX) && !into.has(property)) {
        into.set(property.slice(PREFIX.length), rule.style.getPropertyValue(property).trim());
      }
    }
  };

  for (const sheet of Array.from(document.styleSheets)) {
    let rules: CSSRuleList;
    try {
      rules = sheet.cssRules;
    } catch {
      // 교차 출처 스타일시트 — 우리 토큰은 동일 출처이므로 건너뛴다.
      continue;
    }

    for (const rule of Array.from(rules)) {
      if (rule instanceof CSSStyleRule) {
        collect(rule, base);
      } else if (rule instanceof CSSMediaRule && rule.conditionText.includes("min-width")) {
        // 반응형 오버라이드. `prefers-color-scheme` 블록은 조건에 min-width가 없어 걸러진다.
        for (const inner of Array.from(rule.cssRules)) {
          if (inner instanceof CSSStyleRule) {
            collect(inner, pc);
          }
        }
      }
    }
  }

  return { base, pc };
}

const SIZE_PREFIX = /^(border|padding|gap|radius|icon|shadow|backdrop)-/;
const PRIMITIVE_PREFIX = /^(primitive|font-family|font-weight|number)-/;

/**
 * 컴파일된 스타일시트에서 섹션을 구성한다. 계산값이 아니라 **선언 원문**을 읽는 것이
 * 요점 — 계산값은 참조가 이미 풀려 있고 현재 테마·뷰포트 한쪽만 보인다.
 */
export function readCssSections(): CssSection[] {
  const { base, pc } = readRaw();

  const colors: { name: string; light: string; dark: string }[] = [];
  const responsive: string[] = [];
  const size: string[] = [];
  const theme: string[] = [];
  const primitive: string[] = [];

  for (const [name, declaration] of base) {
    if (name.startsWith("color-")) {
      const pair = splitLightDark(declaration);
      colors.push({
        name,
        light: pair?.light ?? declaration,
        dark: pair?.dark ?? declaration,
      });
      continue;
    }
    if (pc.has(name)) {
      responsive.push(name);
      continue;
    }
    if (SIZE_PREFIX.test(name)) {
      size.push(name);
      continue;
    }
    if (name.startsWith("theme-")) {
      theme.push(name);
      continue;
    }
    if (PRIMITIVE_PREFIX.test(name)) {
      primitive.push(name);
      continue;
    }
    // 분류에 없는 새 토큰도 버리지 않는다.
    size.push(name);
  }

  return [
    {
      id: "light",
      label: "Light",
      category: "Mode",
      source: "컴파일된 CSS — light-dark()의 라이트 쪽",
      tokens: colors.map((entry) => toToken(entry.name, entry.light)),
    },
    {
      id: "dark",
      label: "Dark",
      category: "Mode",
      source: "컴파일된 CSS — light-dark()의 다크 쪽",
      tokens: colors.map((entry) => toToken(entry.name, entry.dark)),
    },
    {
      id: "pc",
      label: "PC",
      category: "Responsive",
      source: "컴파일된 CSS — @media (min-width: 768px) 블록",
      tokens: responsive.map((name) => toToken(name, pc.get(name) ?? "")),
    },
    {
      id: "mobile",
      label: "Mobile",
      category: "Responsive",
      source: "컴파일된 CSS — :root 기본값(모바일 우선)",
      tokens: responsive.map((name) => toToken(name, base.get(name) ?? "")),
    },
    { id: "size", label: "Size", category: null, source: "컴파일된 CSS — :root", tokens: size.map((n) => toToken(n, base.get(n) ?? "")) },
    { id: "theme", label: "Theme", category: null, source: "컴파일된 CSS — :root", tokens: theme.map((n) => toToken(n, base.get(n) ?? "")) },
    { id: "primitive", label: "Primitive", category: null, source: "컴파일된 CSS — :root", tokens: primitive.map((n) => toToken(n, base.get(n) ?? "")) },
  ];
}
