import type { ReactNode } from "react";
import {
  isBlogOpenSourceFont,
  safeBlogAsset,
  safeBlogHref,
} from "./blog-content-utils";

function inlineContent(value: string, keyPrefix: string): ReactNode[] {
  const tokenPattern =
    /<blog-style\b[^>]*>|!\[[^\]]*\]\([^)]+\)|\[[^\]]+\]\([^)]+\)|\*\*[^*\n]+\*\*|__[^_\n]+__|\+\+[^+\n]+\+\+|~~[^~\n]+~~|\*[^*\n]+\*|_[^_\n]+_|`[^`\n]+`/g;
  const nodes: ReactNode[] = [];
  let cursor = 0;
  let tokenIndex = 0;

  let match: RegExpExecArray | null;
  while ((match = tokenPattern.exec(value))) {
    const start = match.index;
    if (start > cursor) nodes.push(value.slice(cursor, start));

    const key = `${keyPrefix}-${tokenIndex++}`;
    const token = match[0];
    if (token.startsWith("<blog-style")) {
      const styled = findBlogStyleEnd(value, tokenPattern.lastIndex);
      if (!styled) {
        nodes.push(token);
        cursor = tokenPattern.lastIndex;
        continue;
      }

      const attributes = parseBlogStyleAttributes(token.slice("<blog-style".length, -1));
      const fullToken = value.slice(start, styled.fullEnd);
      nodes.push(
        attributes ? (
          <span key={key} style={attributes.style}>
            {inlineContent(value.slice(tokenPattern.lastIndex, styled.innerEnd), key)}
          </span>
        ) : fullToken,
      );
      cursor = styled.fullEnd;
      tokenPattern.lastIndex = styled.fullEnd;
      continue;
    }

    const image = token.match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)$/);
    const link = token.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)$/);

    if (image) {
      const safe = safeBlogAsset(image[2]);
      nodes.push(
        safe ? (
          // eslint-disable-next-line @next/next/no-img-element
          <img
            key={key}
            src={safe}
            alt={image[1]}
            loading="lazy"
            style={{
              display: "block",
              maxWidth: "100%",
              height: "auto",
              margin: "1.4em auto",
              borderRadius: 10,
            }}
          />
        ) : token,
      );
    } else if (link) {
      const safe = safeBlogHref(link[2]);
      nodes.push(
        safe ? (
          <a
            key={key}
            href={safe.href}
            title={link[3] || undefined}
            style={{ color: "#007ca8", textDecoration: "underline", textUnderlineOffset: "0.15em" }}
            {...(safe.external ? { target: "_blank", rel: "noreferrer" } : {})}
          >
            {inlineContent(link[1], key)}
          </a>
        ) : token,
      );
    } else if (token.startsWith("**") || token.startsWith("__")) {
      nodes.push(<strong key={key}>{inlineContent(token.slice(2, -2), key)}</strong>);
    } else if (token.startsWith("++")) {
      nodes.push(<u key={key}>{inlineContent(token.slice(2, -2), key)}</u>);
    } else if (token.startsWith("~~")) {
      nodes.push(<s key={key}>{inlineContent(token.slice(2, -2), key)}</s>);
    } else if (token.startsWith("*") || token.startsWith("_")) {
      nodes.push(<em key={key}>{inlineContent(token.slice(1, -1), key)}</em>);
    } else if (token.startsWith("`")) {
      nodes.push(<code key={key} style={inlineCodeStyle}>{token.slice(1, -1)}</code>);
    } else {
      nodes.push(token);
    }
    cursor = tokenPattern.lastIndex;
  }

  if (cursor < value.length) nodes.push(value.slice(cursor));
  return nodes;
}

function findBlogStyleEnd(
  value: string,
  contentStart: number,
): { innerEnd: number; fullEnd: number } | null {
  const tagPattern = /<blog-style\b[^>]*>|<\/blog-style>/g;
  tagPattern.lastIndex = contentStart;
  let depth = 1;
  let match: RegExpExecArray | null;

  while ((match = tagPattern.exec(value))) {
    if (match[0].startsWith("</")) {
      depth -= 1;
      if (depth === 0) {
        return { innerEnd: match.index, fullEnd: tagPattern.lastIndex };
      }
    } else {
      depth += 1;
    }
  }
  return null;
}

function parseBlogStyleAttributes(
  value: string,
): { style: { fontFamily?: string; fontSize?: string; color?: string } } | null {
  const attributes = new Map<string, string>();
  const attributePattern = /\s+(font|size|color)="([^"]*)"/g;
  let consumed = "";
  let match: RegExpExecArray | null;

  while ((match = attributePattern.exec(value))) {
    if (attributes.has(match[1])) return null;
    attributes.set(match[1], match[2]);
    consumed += match[0];
  }

  if (consumed.trim() !== value.trim() || attributes.size === 0) return null;

  const style: { fontFamily?: string; fontSize?: string; color?: string } = {};
  const font = attributes.get("font");
  const size = attributes.get("size");
  const color = attributes.get("color");

  if (font) {
    if (!isBlogOpenSourceFont(font)) return null;
    style.fontFamily = `"${font}", sans-serif`;
  }
  if (size) {
    const parsedSize = Number(size);
    if (!/^\d{1,2}$/.test(size) || parsedSize < 10 || parsedSize > 48) return null;
    style.fontSize = `${parsedSize}px`;
  }
  if (color) {
    if (!/^#[\da-f]{3}(?:[\da-f]{3})?$/i.test(color)) return null;
    style.color = color;
  }

  return { style };
}

function isBlockStart(lines: string[], index: number) {
  const line = lines[index];
  return (
    /^:::align=(?:left|center|right)$/.test(line) ||
    /^#{1,4}\s+/.test(line) ||
    /^\s*([-*+]|\d+\.)\s+/.test(line) ||
    /^\s*>\s?/.test(line) ||
    /^(```|~~~)/.test(line) ||
    /^\s*(---+|\*\*\*+|___+)\s*$/.test(line)
  );
}

type TextAlignment = "left" | "center" | "right";

export default function BlogContent({ content }: { content: string }) {
  const lines = content.replace(/\r\n?/g, "\n").split("\n");
  const blocks: ReactNode[] = [];
  let index = 0;
  let pendingAlignment: TextAlignment | null = null;

  while (index < lines.length) {
    const line = lines[index];
    const alignment = line.match(/^:::align=(left|center|right)$/);
    if (alignment) {
      pendingAlignment = alignment[1] as TextAlignment;
      index += 1;
      continue;
    }
    if (!line.trim()) {
      index += 1;
      continue;
    }
    const textAlignment = pendingAlignment ? { textAlign: pendingAlignment } : {};

    const fence = line.match(/^(```|~~~)\s*([\w-]*)\s*$/);
    if (fence) {
      const marker = fence[1];
      const codeLines: string[] = [];
      index += 1;
      while (index < lines.length && !lines[index].startsWith(marker)) {
        codeLines.push(lines[index]);
        index += 1;
      }
      if (index < lines.length) index += 1;
      blocks.push(
        <pre key={`block-${blocks.length}`} style={{ ...preStyle, ...textAlignment }}>
          <code style={inlineCodeStyle}>{codeLines.join("\n")}</code>
        </pre>,
      );
      pendingAlignment = null;
      continue;
    }

    const heading = line.match(/^(#{1,4})\s+(.+?)\s*#*\s*$/);
    if (heading) {
      const level = Math.max(2, heading[1].length);
      const children = inlineContent(heading[2], `heading-${index}`);
      if (level === 2) blocks.push(<h2 key={`block-${blocks.length}`} style={{ ...headingTwoStyle, ...textAlignment }}>{children}</h2>);
      else if (level === 3) blocks.push(<h3 key={`block-${blocks.length}`} style={{ ...headingThreeStyle, ...textAlignment }}>{children}</h3>);
      else blocks.push(<h4 key={`block-${blocks.length}`} style={{ ...headingFourStyle, ...textAlignment }}>{children}</h4>);
      index += 1;
      pendingAlignment = null;
      continue;
    }

    if (/^\s*(---+|\*\*\*+|___+)\s*$/.test(line)) {
      blocks.push(<hr key={`block-${blocks.length}`} style={separatorStyle} />);
      index += 1;
      pendingAlignment = null;
      continue;
    }

    if (/^\s*>\s?/.test(line)) {
      const quoteLines: string[] = [];
      while (index < lines.length && /^\s*>\s?/.test(lines[index])) {
        quoteLines.push(lines[index].replace(/^\s*>\s?/, ""));
        index += 1;
      }
      blocks.push(
        <blockquote key={`block-${blocks.length}`} style={{ ...quoteStyle, ...textAlignment }}>
          {quoteLines.map((item, quoteIndex) => (
            <p key={quoteIndex} style={paragraphStyle}>{inlineContent(item, `quote-${index}-${quoteIndex}`)}</p>
          ))}
        </blockquote>,
      );
      pendingAlignment = null;
      continue;
    }

    const listMarker = line.match(/^\s*(?:([-*+])|(\d+)\.)\s+/);
    if (listMarker) {
      const ordered = Boolean(listMarker[2]);
      const items: ReactNode[] = [];
      while (index < lines.length) {
        const item = lines[index].match(/^\s*(?:([-*+])|(\d+)\.)\s+(.+)$/);
        if (!item || Boolean(item[2]) !== ordered) break;
        items.push(
          <li key={`item-${index}`} style={{ marginTop: "0.35em" }}>
            {inlineContent(item[3], `list-${index}`)}
          </li>,
        );
        index += 1;
      }
      if (ordered) blocks.push(<ol key={`block-${blocks.length}`} style={{ ...listStyle, ...textAlignment }}>{items}</ol>);
      else blocks.push(<ul key={`block-${blocks.length}`} style={{ ...listStyle, ...textAlignment }}>{items}</ul>);
      pendingAlignment = null;
      continue;
    }

    const paragraph: string[] = [];
    while (index < lines.length && lines[index].trim() && !isBlockStart(lines, index)) {
      paragraph.push(lines[index]);
      index += 1;
    }
    if (paragraph.length === 0) {
      paragraph.push(lines[index]);
      index += 1;
    }
    blocks.push(
      <p key={`block-${blocks.length}`} style={{ ...paragraphStyle, ...textAlignment }}>
        {inlineContent(paragraph.join(" "), `paragraph-${index}`)}
      </p>,
    );
    pendingAlignment = null;
  }

  return <div>{blocks}</div>;
}

const paragraphStyle = { margin: "0 0 1.1em" };
const listStyle = { margin: "0 0 1.1em", paddingLeft: "1.5em" };
const headingTwoStyle = { margin: "1.7em 0 0.55em", color: "#102033", lineHeight: 1.3 };
const headingThreeStyle = { ...headingTwoStyle, fontSize: "1.2em" };
const headingFourStyle = { ...headingTwoStyle, fontSize: "1.05em" };
const inlineCodeStyle = { padding: "0.12em 0.35em", borderRadius: 4, background: "#f0f3f5", fontSize: "0.9em" };
const preStyle = { overflowX: "auto" as const, margin: "0 0 1.1em", padding: "1em", borderRadius: 10, background: "#f0f3f5", lineHeight: 1.6 };
const quoteStyle = { margin: "0 0 1.1em", padding: "0.2em 0 0.2em 1em", borderLeft: "3px solid #00a7c9", color: "#526176" };
const separatorStyle = { margin: "2em 0", border: 0, borderTop: "1px solid #dce3e8" };
