import hljs from "highlight.js";
import React from "react";
import { readFileSync } from "fs";
import { Span } from "paintcannon-react";
import {
  SYNTAX_ATTRIBUTE_COLOR,
  SYNTAX_BUILT_IN_COLOR,
  SYNTAX_COMMENT_COLOR,
  SYNTAX_KEYWORD_COLOR,
  SYNTAX_LITERAL_COLOR,
  SYNTAX_NAME_COLOR,
  SYNTAX_NUMBER_COLOR,
  SYNTAX_PROPERTY_COLOR,
  SYNTAX_SELECTOR_CLASS_COLOR,
  SYNTAX_SELECTOR_ID_COLOR,
  SYNTAX_SELECTOR_TAG_COLOR,
  SYNTAX_STRING_COLOR,
  SYNTAX_TITLE_COLOR,
  SYNTAX_TYPE_COLOR,
  SYNTAX_VALUE_COLOR,
  SYNTAX_VARIABLE_COLOR,
} from "../theme.ts";
export function HighlightedCode({ code, language }: { code: string; language?: string }) {
  try {
    let result;
    if (language && hljs.getLanguage(language)) {
      result = hljs.highlight(code, {
        language,
      });
    } else {
      result = hljs.highlightAuto(code);
    }
    const segments = parseHighlightedHTML(result.value);
    return <CodeSegments segments={segments} />;
  } catch (error) {
    // If highlighting fails, return plain text lines
    return (
      <>
        {code.split("\n").map((line, index) => (
          <Span key={`code-failed-${index}`}>{line}</Span>
        ))}
      </>
    );
  }
}
type CodeSegment = {
  text: string;
  className?: string;
};
function parseHighlightedHTML(html: string): CodeSegment[] {
  const segments: CodeSegment[] = [];
  let currentIndex = 0;

  // Simple state machine to parse HTML
  while (currentIndex < html.length) {
    const nextOpenTag = html.indexOf('<span class="', currentIndex);
    if (nextOpenTag === -1) {
      // No more spans, add remaining text
      const remainingText = html.substring(currentIndex);
      if (remainingText) {
        segments.push({
          text: decodeHtmlEntities(remainingText),
        });
      }
      break;
    }

    // Add any text before the span
    if (nextOpenTag > currentIndex) {
      const textBefore = html.substring(currentIndex, nextOpenTag);
      segments.push({
        text: decodeHtmlEntities(textBefore),
      });
    }

    // Find the end of the opening tag
    const classStart = nextOpenTag + 13; // '<span class="'.length
    const classEnd = html.indexOf('"', classStart);
    const tagEnd = html.indexOf(">", classEnd);
    if (classEnd === -1 || tagEnd === -1) break;
    const className = html.substring(classStart, classEnd);

    // Find the closing tag
    const closingTag = "</span>";
    const contentStart = tagEnd + 1;
    let closingTagStart = html.indexOf(closingTag, contentStart);

    // Handle nested spans by counting open/close tags
    let openCount = 1;
    let searchFrom = contentStart;
    while (openCount > 0 && closingTagStart !== -1) {
      const nextOpen = html.indexOf("<span", searchFrom);
      if (nextOpen !== -1 && nextOpen < closingTagStart) {
        openCount++;
        searchFrom = nextOpen + 5;
      } else {
        openCount--;
        if (openCount > 0) {
          searchFrom = closingTagStart + closingTag.length;
          closingTagStart = html.indexOf(closingTag, searchFrom);
        }
      }
    }
    if (closingTagStart === -1) break;
    const content = html.substring(contentStart, closingTagStart);

    // Recursively parse content for nested spans
    if (content.includes("<span")) {
      const nestedSegments = parseHighlightedHTML(content);
      segments.push(...nestedSegments);
    } else {
      segments.push({
        text: decodeHtmlEntities(content),
        className: className,
      });
    }
    currentIndex = closingTagStart + closingTag.length;
  }
  return segments;
}
function CodeSegments({ segments }: { segments: CodeSegment[] }) {
  const lines: CodeSegment[][] = [];
  let currentLine: CodeSegment[] = [];
  segments.forEach(segment => {
    const linesInSegment = segment.text.split("\n");
    linesInSegment.forEach((lineText, lineIndex) => {
      if (lineIndex > 0) {
        // This is a new line, push current line and start fresh
        lines.push(currentLine);
        currentLine = [];
      }
      if (lineText || lineIndex === linesInSegment.length - 1) {
        currentLine.push({
          text: lineText,
          className: segment.className,
        });
      }
    });
  });

  // Push the last line if it has content
  if (currentLine.length > 0) {
    lines.push(currentLine);
  }
  return (
    <>
      {lines.map((lineSegments, index) => (
        <CodeLine segments={lineSegments} key={`code-${index}`} />
      ))}
    </>
  );
}
function CodeLine({ segments }: { segments: CodeSegment[] }) {
  return (
    <Span>
      {segments.map((segment, index) => {
        const color = segment.className ? getColorForClass(segment.className) : undefined;
        return (
          <Span
            key={index}
            style={{
              color: color,
            }}
          >
            {segment.text}
          </Span>
        );
      })}
    </Span>
  );
}
function decodeHtmlEntities(text: string): string {
  return text
    .replace(/&quot;/g, '"')
    .replace(/&#x27;/g, "'")
    .replace(/&#39;/g, "'")
    .replace(/&apos;/g, "'")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&amp;/g, "&");
}
function getColorForClass(className: string): string | undefined {
  const colorMap: Record<string, string> = {
    "hljs-keyword": SYNTAX_KEYWORD_COLOR,
    "hljs-string": SYNTAX_STRING_COLOR,
    "hljs-comment": SYNTAX_COMMENT_COLOR,
    "hljs-number": SYNTAX_NUMBER_COLOR,
    "hljs-title": SYNTAX_TITLE_COLOR,
    "hljs-title function_": SYNTAX_TITLE_COLOR,
    "hljs-variable": SYNTAX_VARIABLE_COLOR,
    "hljs-type": SYNTAX_TYPE_COLOR,
    "hljs-attr": SYNTAX_ATTRIBUTE_COLOR,
    "hljs-built_in": SYNTAX_BUILT_IN_COLOR,
    "hljs-literal": SYNTAX_LITERAL_COLOR,
    "hljs-name": SYNTAX_NAME_COLOR,
    "hljs-selector-tag": SYNTAX_SELECTOR_TAG_COLOR,
    "hljs-selector-class": SYNTAX_SELECTOR_CLASS_COLOR,
    "hljs-selector-id": SYNTAX_SELECTOR_ID_COLOR,
    "hljs-property": SYNTAX_PROPERTY_COLOR,
    "hljs-value": SYNTAX_VALUE_COLOR,
  };
  return colorMap[className];
}
