// @ts-nocheck
// Animated code beat — windowed type-on block with lightweight token coloring.
// Language-agnostic regex highlighting, blinking caret at the reveal head,
// and accent-tinted glow rows. Deterministic (frame-seeded).
import React from "react";
import { useCurrentFrame, useVideoConfig } from "remotion";
import { MONO } from "../fonts";
import { C, withAlpha } from "../theme";
import { beat, EASE } from "../motion";

type Tone = "dark" | "light";

type Token = { text: string; color: string };

// A frozen, ordered set of regexes. Order matters: comments and strings must
// win over keywords/numbers so we don't recolor their interiors.
const KEYWORDS =
  /^(if|else|const|let|var|return|function|import|export|from|for|while|class|new|await|async|default|=>)\b/;

const tokenColors = (tone: Tone) => {
  // Theme-aware palette: dark scenes lean on the orange tokens, light (cream)
  // scenes need darker, higher-contrast token colors.
  if (tone === "light") {
    return {
      base: "#2b2118",
      comment: "#9a8e7d",
      string: "#1f7a4d",
      number: "#b5651d",
      keyword: "#c0561a",
      punct: "#7a6f60",
    };
  }
  return {
    base: C.text,
    comment: C.textFaint,
    string: C.cool,
    number: C.accent2,
    keyword: C.accent,
    punct: C.textDim,
  };
};

// Tokenize a single line of code into colored spans. Generic, not tied to any
// language — keywords, strings, // comments, numbers, punctuation.
const tokenizeLine = (line: string, pal: ReturnType<typeof tokenColors>): Token[] => {
  const out: Token[] = [];
  let rest = line;

  // Whole-line comment fast path (after leading whitespace).
  const commentIdx = rest.indexOf("//");
  let codePart = rest;
  let commentPart = "";
  if (commentIdx >= 0) {
    codePart = rest.slice(0, commentIdx);
    commentPart = rest.slice(commentIdx);
  }

  rest = codePart;
  while (rest.length > 0) {
    // Leading whitespace.
    const ws = rest.match(/^\s+/);
    if (ws) {
      out.push({ text: ws[0], color: pal.base });
      rest = rest.slice(ws[0].length);
      continue;
    }
    // Strings (single, double, backtick).
    const str = rest.match(/^(["'`])(?:\\.|(?!\1).)*\1?/);
    if (str) {
      out.push({ text: str[0], color: pal.string });
      rest = rest.slice(str[0].length);
      continue;
    }
    // Identifiers / keywords.
    const word = rest.match(/^[A-Za-z_$][A-Za-z0-9_$]*/);
    if (word) {
      const isKw = KEYWORDS.test(word[0] + " ");
      out.push({ text: word[0], color: isKw ? pal.keyword : pal.base });
      rest = rest.slice(word[0].length);
      continue;
    }
    // The fat-arrow keyword.
    if (rest.startsWith("=>")) {
      out.push({ text: "=>", color: pal.keyword });
      rest = rest.slice(2);
      continue;
    }
    // Numbers.
    const num = rest.match(/^(?:0x[0-9a-fA-F]+|\d+\.?\d*)/);
    if (num) {
      out.push({ text: num[0], color: pal.number });
      rest = rest.slice(num[0].length);
      continue;
    }
    // Punctuation / operators.
    const punct = rest.match(/^[^\sA-Za-z0-9_$"'`]+/);
    if (punct) {
      out.push({ text: punct[0], color: pal.punct });
      rest = rest.slice(punct[0].length);
      continue;
    }
    // Fallback (should not hit): consume one char.
    out.push({ text: rest[0], color: pal.base });
    rest = rest.slice(1);
  }

  if (commentPart) out.push({ text: commentPart, color: pal.comment });
  return out;
};

// Slice a tokenized line to a given visible character count, preserving colors.
const sliceTokens = (tokens: Token[], visible: number): Token[] => {
  if (visible >= Infinity) return tokens;
  const out: Token[] = [];
  let remaining = visible;
  for (const tok of tokens) {
    if (remaining <= 0) break;
    if (tok.text.length <= remaining) {
      out.push(tok);
      remaining -= tok.text.length;
    } else {
      out.push({ text: tok.text.slice(0, remaining), color: tok.color });
      remaining = 0;
    }
  }
  return out;
};

export const CodeWindow: React.FC<{
  code: string;
  title?: string;
  tone?: Tone;
  at?: number; // type-on start (seconds)
  cps?: number; // chars/sec (0 = instant)
  highlight?: number[]; // 1-based line numbers to glow
  accent?: string;
  width?: number | string;
  style?: React.CSSProperties;
}> = ({
  code,
  title = "",
  tone = "dark",
  at = 0,
  cps = 42,
  highlight = [],
  accent = C.accent,
  width,
  style,
}) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const t = frame / fps;

  const lines = code.replace(/\t/g, "  ").split("\n");
  const pal = tokenColors(tone);
  const isLight = tone === "light";

  // Total characters revealed so far (across the whole block). cps=0 = instant.
  const revealed =
    cps <= 0 ? Infinity : Math.max(0, Math.floor((t - at) * cps));

  // Window chrome colors.
  const winBg = isLight ? "#fbf6ee" : C.bg1;
  const winBorder = isLight ? "rgba(0,0,0,0.10)" : C.glassBorder;
  const barBg = isLight ? "rgba(0,0,0,0.035)" : C.bg2;
  const gutterColor = isLight ? "#b3a896" : C.textFaint;
  const titleColor = isLight ? "#6b6052" : C.textDim;

  // Walk lines, tracking the running character offset so the global reveal
  // counter maps onto per-line visible counts and the caret head.
  let runningOffset = 0;
  const caretLineIdx = (() => {
    if (revealed === Infinity) return -1;
    let acc = 0;
    for (let i = 0; i < lines.length; i++) {
      const len = lines[i].length;
      if (revealed <= acc + len) return i;
      acc += len + 1; // +1 for the implicit newline
    }
    return lines.length - 1;
  })();

  const caretOn = revealed !== Infinity && t >= at && Math.floor(t * 2.4) % 2 === 0;
  const highlightSet = new Set(highlight);

  const fontSize = 26;
  const lineH = 1.6;

  return (
    <div
      style={{
        width: width ?? 760,
        borderRadius: 16,
        overflow: "hidden",
        background: winBg,
        border: `1px solid ${winBorder}`,
        boxShadow: isLight
          ? "0 24px 60px rgba(0,0,0,0.16)"
          : `0 30px 80px rgba(0,0,0,0.55)`,
        fontFamily: MONO,
        ...style,
      }}
    >
      {/* Titlebar with traffic lights. */}
      <div
        style={{
          display: "flex",
          alignItems: "center",
          gap: 8,
          padding: "12px 16px",
          background: barBg,
          borderBottom: `1px solid ${winBorder}`,
        }}
      >
        {["#ff5f56", "#ffbd2e", "#27c93f"].map((c) => (
          <div
            key={c}
            style={{ width: 12, height: 12, borderRadius: 999, background: c }}
          />
        ))}
        {title ? (
          <div
            style={{
              marginLeft: 10,
              fontSize: 18,
              fontWeight: 400,
              color: titleColor,
              letterSpacing: "0.02em",
            }}
          >
            {title}
          </div>
        ) : null}
      </div>

      {/* Code body. */}
      <div style={{ padding: "18px 0", position: "relative" }}>
        {lines.map((line, i) => {
          const lineNo = i + 1;
          const lineStart = runningOffset;
          runningOffset += line.length + 1;

          const visible =
            revealed === Infinity
              ? Infinity
              : Math.max(0, Math.min(line.length, revealed - lineStart));

          const tokens = sliceTokens(tokenizeLine(line, pal), visible);
          const showCaret = caretOn && i === caretLineIdx;

          const glow = highlightSet.has(lineNo)
            ? beat(frame, fps, at + 0.1, at + 0.6, EASE.out)
            : 0;

          return (
            <div
              key={i}
              style={{
                display: "flex",
                alignItems: "flex-start",
                fontSize,
                lineHeight: lineH,
                background:
                  glow > 0 ? withAlpha(accent, 0.1 * glow) : "transparent",
                boxShadow:
                  glow > 0
                    ? `inset 3px 0 0 ${withAlpha(accent, glow)}`
                    : undefined,
              }}
            >
              {/* Gutter line number. */}
              <div
                style={{
                  width: 56,
                  flexShrink: 0,
                  textAlign: "right",
                  paddingRight: 18,
                  color: gutterColor,
                  userSelect: "none",
                  fontVariantNumeric: "tabular-nums",
                }}
              >
                {lineNo}
              </div>
              {/* Code content. */}
              <div
                style={{
                  flex: 1,
                  paddingRight: 24,
                  whiteSpace: "pre-wrap",
                  wordBreak: "break-word",
                  color: pal.base,
                  minHeight: fontSize * lineH,
                }}
              >
                {tokens.map((tok, j) => (
                  <span key={j} style={{ color: tok.color }}>
                    {tok.text}
                  </span>
                ))}
                {showCaret ? (
                  <span
                    style={{
                      display: "inline-block",
                      width: "0.55em",
                      marginLeft: 1,
                      transform: "translateY(0.12em)",
                      background: accent,
                      height: "1.05em",
                      borderRadius: 1,
                    }}
                  />
                ) : null}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};