import { useEffect, useState } from "react";
import api from "../api/client";

const NOTE_LABELS = "ABCDEFGHIJKLMNOP";

function parseBellNotation(notation) {
  const raw = String(notation || "").trim();
  if (raw === "0" || !raw) return 0;
  let value = 0;
  for (const part of raw.split("+")) {
    const n = Number.parseInt(part.trim(), 10);
    if (Number.isInteger(n) && n >= 1 && n <= 16) value |= 1 << (n - 1);
  }
  return value;
}

function parseStepsString(stepsStr) {
  if (!stepsStr || !String(stepsStr).trim()) return [];
  return String(stepsStr).trim().split(",").map((s) => parseBellNotation(s));
}

function interpolateHue(t) {
  const stops = [
    [0.0, 190],
    [0.24, 140],
    [0.5, 56],
    [0.82, 30],
    [1.0, 0],
  ];
  for (let i = 0; i < stops.length - 1; i++) {
    const [aPos, aHue] = stops[i];
    const [bPos, bHue] = stops[i + 1];
    if (t >= aPos && t <= bPos) {
      const local = (t - aPos) / (bPos - aPos || 1);
      return aHue + (bHue - aHue) * local;
    }
  }
  return stops[stops.length - 1][1];
}

function noteDotColor(noteNumber) {
  const n = Number(noteNumber || 1);
  const t = Math.min(1, Math.max(0, (n - 1) / 15));
  const hue = interpolateHue(t);
  return `hsl(${hue}, 78%, 68%)`;
}

function noteDotGlow(noteNumber) {
  const n = Number(noteNumber || 1);
  const t = Math.min(1, Math.max(0, (n - 1) / 15));
  const hue = interpolateHue(t);
  return `hsla(${hue}, 78%, 56%, 0.45)`;
}

function noteDotColorFromSettings(noteNumber, colorPalette) {
  const n = Number(noteNumber || 1);
  const custom = colorPalette?.[n - 1];
  return custom || noteDotColor(n);
}

function noteDotGlowFromSettings(noteNumber, colorPalette) {
  const n = Number(noteNumber || 1);
  const custom = colorPalette?.[n - 1];
  return custom ? `${custom}66` : noteDotGlow(n);
}

function normalizeFileUrl(url) {
  if (!url || typeof url !== "string") return null;
  if (url.startsWith("http") || url.startsWith("/api")) return url;
  if (url.startsWith("/")) return `/api${url}`;
  return `/api/${url}`;
}

async function fetchBinaryResponse(url) {
  const token = localStorage.getItem("access_token");
  try {
    const res = await fetch(url, {
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    });
    if (res.ok) return res;
    if (url.startsWith("http")) {
      const retry = await fetch(url);
      if (retry.ok) return retry;
      throw new Error(`Failed to fetch binary: ${retry.statusText || retry.status}`);
    }
    throw new Error(`Failed to fetch binary: ${res.statusText || res.status}`);
  } catch {
    if (url.startsWith("http")) {
      const retry = await fetch(url);
      if (retry.ok) return retry;
      throw new Error(`Failed to fetch binary: ${retry.statusText || retry.status}`);
    }
    throw new Error("Failed to fetch binary");
  }
}

async function decodeBsmBinary(url) {
  const res = await fetchBinaryResponse(url);
  const buf = await res.arrayBuffer();
  const view = new DataView(buf);
  const steps = [];
  for (let i = 0; i + 1 < buf.byteLength; i += 2) {
    steps.push(view.getUint16(i, false));
  }
  return steps;
}

export default function BinaryTableModal({ open, melody, builtMelody, files, archetypeCsv, onClose }) {
  const info = melody?.information || {};
  const [steps, setSteps] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [noteColors, setNoteColors] = useState([]);

  useEffect(() => {
    if (!open) {
      setSteps([]);
      setLoading(false);
      setError("");
      return;
    }

    const binaryUrlCandidate = builtMelody?.binary_url
      ? `/api${builtMelody.binary_url}`
      : files?.binary_url || melody?.url || null;
    const binaryUrl = normalizeFileUrl(binaryUrlCandidate);
    const csv = archetypeCsv || info.archetype_csv || null;

    if (binaryUrl) {
      setLoading(true);
      setError("");
      decodeBsmBinary(binaryUrl)
        .then((decoded) => setSteps(decoded))
        .catch((err) => {
          if (csv) {
            setSteps(parseStepsString(csv));
            setError("");
            return;
          }
          setError(err.message || "Failed to load melody data.");
        })
        .finally(() => setLoading(false));
      return;
    }

    if (csv) {
      setSteps(parseStepsString(csv));
      setError("");
      return;
    }

    setError("No binary or archetype data available for this melody.");
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    if (!open) return;
    let canceled = false;
    api.get("/settings/melody")
      .then((s) => {
        if (canceled) return;
        setNoteColors((s?.note_assignment_colors || []).slice(0, 16));
      })
      .catch(() => {
        if (!canceled) setNoteColors([]);
      });
    return () => { canceled = true; };
  }, [open]);

  if (!open) return null;

  const detectedNoteCount = steps.reduce((max, stepValue) => {
    let highest = 0;
    for (let bit = 15; bit >= 0; bit--) {
      if (stepValue & (1 << bit)) {
        highest = bit + 1;
        break;
      }
    }
    return Math.max(max, highest);
  }, 0);
  const gridNoteCount = Math.max(1, Math.min(16, Number(info.totalNotes || 0) || detectedNoteCount || 1));

  return (
    <div
      className="fixed inset-0 flex items-center justify-center z-50"
      style={{ backgroundColor: "rgba(0,0,0,0.7)" }}
      onClick={(e) => e.target === e.currentTarget && onClose()}
    >
      <div
        className="w-full rounded-lg border shadow-xl"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", maxWidth: "1100px" }}
      >
        <div className="flex items-center justify-between px-6 py-4 border-b" style={{ borderColor: "var(--border-primary)" }}>
          <div>
            <h2 className="text-lg font-semibold" style={{ color: "var(--text-heading)" }}>Archetype View</h2>
            <p className="text-xs mt-0.5" style={{ color: "var(--text-muted)" }}>{steps.length} steps</p>
          </div>
          <button onClick={onClose} className="text-xl leading-none" style={{ color: "var(--text-muted)" }}>&times;</button>
        </div>

        <div className="px-6 py-5 space-y-4">
          {loading && <p className="text-sm text-center py-4" style={{ color: "var(--text-muted)" }}>Loading binary...</p>}
          {error && (
            <div
              className="text-sm rounded-md p-3 border"
              style={{ backgroundColor: "var(--danger-bg)", borderColor: "var(--danger)", color: "var(--danger-text)" }}
            >
              {error}
            </div>
          )}

          {!loading && !error && steps.length > 0 && (
            <div className="rounded-md border overflow-auto" style={{ borderColor: "var(--border-primary)", maxHeight: "520px" }}>
              <table className="min-w-max border-separate border-spacing-0 text-xs">
                <thead>
                  <tr>
                    <th
                      className="sticky top-0 left-0 z-20 px-2 py-1.5 text-left border-b border-r"
                      style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-primary)", color: "var(--text-muted)" }}
                    >
                      Note \ Step
                    </th>
                    {steps.map((_, stepIdx) => (
                      <th
                        key={stepIdx}
                        className="sticky top-0 z-10 px-2 py-1.5 text-center border-b border-r"
                        style={{ minWidth: "40px", backgroundColor: "var(--bg-primary)", borderColor: "var(--border-primary)", color: "var(--text-muted)" }}
                      >
                        {stepIdx + 1}
                      </th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {Array.from({ length: gridNoteCount }, (_, noteIdx) => (
                    <tr key={noteIdx}>
                      <th
                        className="sticky left-0 z-[1] px-2 py-1.5 text-left border-b border-r"
                        style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-primary)", color: "var(--text-secondary)" }}
                      >
                        {NOTE_LABELS[noteIdx]}
                      </th>
                      {steps.map((stepValue, stepIdx) => {
                        const enabled = Boolean(stepValue & (1 << noteIdx));
                        return (
                          <td
                            key={`${noteIdx}-${stepIdx}`}
                            className="border-b border-r"
                            style={{ width: "40px", height: "40px", borderColor: "var(--border-primary)" }}
                          >
                            <span className="w-full h-full flex items-center justify-center" aria-hidden="true">
                              <span
                                style={{
                                  width: "60%",
                                  height: "60%",
                                  borderRadius: "9999px",
                                  backgroundColor: noteDotColorFromSettings(noteIdx + 1, noteColors),
                                  opacity: enabled ? 1 : 0,
                                  transform: enabled ? "scale(1)" : "scale(0.4)",
                                  boxShadow: enabled ? `0 0 10px 3px ${noteDotGlowFromSettings(noteIdx + 1, noteColors)}` : "none",
                                  transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease",
                                }}
                              />
                            </span>
                          </td>
                        );
                      })}
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>

        <div className="flex justify-end px-6 py-4 border-t" style={{ borderColor: "var(--border-primary)" }}>
          <button
            onClick={onClose}
            className="px-4 py-2 text-sm rounded-md transition-colors"
            style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}
          >
            Close
          </button>
        </div>
      </div>
    </div>
  );
}
