import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import api from "../api/client";

const MAX_NOTES = 16;
const NOTE_LABELS = "ABCDEFGHIJKLMNOP";

function bellFrequency(bellNumber) {
  return 880 * Math.pow(Math.pow(2, 1 / 12), -2 * (bellNumber - 1));
}

function stepToNotation(stepValue) {
  if (!stepValue) return "0";
  const active = [];
  for (let bit = 0; bit < 16; bit++) {
    if (stepValue & (1 << bit)) active.push(bit + 1);
  }
  return active.join("+");
}

function stepToHex(stepValue) {
  return `0x${(stepValue >>> 0).toString(16).toUpperCase().padStart(4, "0")}`;
}

function playStep(audioCtx, stepValue, noteDurationMs) {
  if (!audioCtx) return;

  const now = audioCtx.currentTime;
  const duration = Math.max(10, noteDurationMs) / 1000;
  const fadeIn = 0.005;
  const fadeOut = Math.min(0.03, duration / 2);

  for (let bit = 0; bit < 16; bit++) {
    if (stepValue & (1 << bit)) {
      const freq = bellFrequency(bit + 1);
      const osc = audioCtx.createOscillator();
      const gain = audioCtx.createGain();

      osc.type = "sine";
      osc.frequency.setValueAtTime(freq, now);
      gain.gain.setValueAtTime(0, now);
      gain.gain.linearRampToValueAtTime(0.3, now + fadeIn);
      gain.gain.setValueAtTime(0.3, now + Math.max(duration - fadeOut, fadeIn + 0.001));
      gain.gain.linearRampToValueAtTime(0, now + duration);

      osc.connect(gain);
      gain.connect(audioCtx.destination);

      osc.start(now);
      osc.stop(now + duration);
    }
  }
}

export default function MelodyComposer() {
  const navigate = useNavigate();
  const [steps, setSteps] = useState(Array.from({ length: 16 }, () => 0));
  const [noteCount, setNoteCount] = useState(8);
  const [stepDelayMs, setStepDelayMs] = useState(280);
  const [noteDurationMs, setNoteDurationMs] = useState(110);
  const [loopEnabled, setLoopEnabled] = useState(true);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentStep, setCurrentStep] = useState(-1);
  const [error, setError] = useState("");
  const [successMsg, setSuccessMsg] = useState("");
  const [showDeployModal, setShowDeployModal] = useState(false);
  const [deployName, setDeployName] = useState("");
  const [deployPid, setDeployPid] = useState("");
  const [deployError, setDeployError] = useState("");
  const [deploying, setDeploying] = useState(false);

  const audioCtxRef = useRef(null);
  const playbackRef = useRef(null);
  const stepsRef = useRef(steps);
  const stepDelayRef = useRef(stepDelayMs);
  const noteDurationRef = useRef(noteDurationMs);
  const loopEnabledRef = useRef(loopEnabled);

  useEffect(() => {
    stepsRef.current = steps;
  }, [steps]);

  useEffect(() => {
    stepDelayRef.current = stepDelayMs;
  }, [stepDelayMs]);

  useEffect(() => {
    noteDurationRef.current = noteDurationMs;
  }, [noteDurationMs]);

  useEffect(() => {
    loopEnabledRef.current = loopEnabled;
  }, [loopEnabled]);

  const ensureAudioContext = useCallback(() => {
    if (!audioCtxRef.current || audioCtxRef.current.state === "closed") {
      audioCtxRef.current = new (window.AudioContext || window.webkitAudioContext)();
    }
    if (audioCtxRef.current.state === "suspended") {
      audioCtxRef.current.resume();
    }
    return audioCtxRef.current;
  }, []);

  const stopPlayback = useCallback(() => {
    if (playbackRef.current?.timer) {
      clearTimeout(playbackRef.current.timer);
    }
    playbackRef.current = null;
    setIsPlaying(false);
    setCurrentStep(-1);
  }, []);

  const scheduleStep = useCallback((stepIndex) => {
    const currentSteps = stepsRef.current;
    if (!currentSteps.length) {
      stopPlayback();
      return;
    }

    const ctx = ensureAudioContext();
    const nextIndex = stepIndex % currentSteps.length;
    const stepValue = currentSteps[nextIndex];

    setCurrentStep(nextIndex);
    playStep(ctx, stepValue, noteDurationRef.current);

    const isLastStep = nextIndex >= currentSteps.length - 1;
    const shouldContinue = !isLastStep || loopEnabledRef.current;

    if (!shouldContinue) {
      playbackRef.current = {
        timer: setTimeout(() => stopPlayback(), stepDelayRef.current),
      };
      return;
    }

    playbackRef.current = {
      timer: setTimeout(() => {
        scheduleStep(isLastStep ? 0 : nextIndex + 1);
      }, stepDelayRef.current),
    };
  }, [ensureAudioContext, stopPlayback]);

  useEffect(() => {
    return () => {
      stopPlayback();
    };
  }, [stopPlayback]);

  const toggleCell = (noteIndex, stepIndex) => {
    const bit = 1 << noteIndex;
    setSteps((prev) => {
      const next = [...prev];
      next[stepIndex] = (next[stepIndex] || 0) ^ bit;
      return next;
    });
  };

  const addStep = () => setSteps((prev) => [...prev, 0]);
  const removeStep = () => {
    setSteps((prev) => {
      if (prev.length <= 1) return prev;
      const next = prev.slice(0, prev.length - 1);
      if (currentStep >= next.length) setCurrentStep(next.length - 1);
      return next;
    });
  };

  const addNote = () => setNoteCount((prev) => Math.min(MAX_NOTES, prev + 1));
  const removeNote = () => {
    setNoteCount((prev) => {
      if (prev <= 1) return prev;
      const nextCount = prev - 1;
      const removedBitMask = ~((1 << nextCount) - 1);
      setSteps((currentSteps) => currentSteps.map((value) => value & ~removedBitMask));
      return nextCount;
    });
  };

  const clearAll = () => setSteps((prev) => prev.map(() => 0));

  const handlePlay = () => {
    if (!stepsRef.current.length) return;
    setError("");
    setIsPlaying(true);
    scheduleStep(0);
  };

  const openDeployModal = () => {
    setError("");
    setSuccessMsg("");
    setDeployError("");
    setShowDeployModal(true);
  };

  const closeDeployModal = () => {
    if (deploying) return;
    setDeployError("");
    setShowDeployModal(false);
  };

  const handleDeploy = async () => {
    const name = deployName.trim();
    const pid = deployPid.trim();
    if (!name) {
      setDeployError("Name is required.");
      return;
    }
    if (!pid) {
      setDeployError("PID is required.");
      return;
    }

    setDeploying(true);
    setDeployError("");
    setSuccessMsg("");
    try {
      const existing = await api.get("/builder/melodies");
      const list = existing.melodies || [];
      const dupName = list.find((m) => m.name.toLowerCase() === name.toLowerCase());
      if (dupName) {
        setDeployError(`An archetype with the name "${name}" already exists.`);
        return;
      }
      const dupPid = list.find((m) => m.pid && m.pid.toLowerCase() === pid.toLowerCase());
      if (dupPid) {
        setDeployError(`An archetype with the PID "${pid}" already exists.`);
        return;
      }

      const stepsStr = steps.map(stepToNotation).join(",");
      const created = await api.post("/builder/melodies", {
        name,
        pid,
        steps: stepsStr,
      });

      setSuccessMsg(`Archetype "${name}" deployed successfully.`);
      setShowDeployModal(false);
      setDeployName("");
      setDeployPid("");
      if (created?.id) {
        navigate(`/melodies/archetypes/${created.id}`);
      }
    } catch (err) {
      setDeployError(err.message);
    } finally {
      setDeploying(false);
    }
  };

  const activeBellsInCurrentStep = useMemo(() => {
    if (currentStep < 0 || !steps[currentStep]) return [];
    const active = [];
    for (let bit = 0; bit < noteCount; bit++) {
      if (steps[currentStep] & (1 << bit)) active.push(bit + 1);
    }
    return active;
  }, [currentStep, noteCount, steps]);

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-2xl font-bold" style={{ color: "var(--text-heading)" }}>
          Melody Composer
        </h1>
        <p className="text-sm mt-1" style={{ color: "var(--text-muted)" }}>
          Build bell-step melodies visually. Notes map directly to bell numbers (1-16).
        </p>
      </div>

      {error && (
        <div
          className="text-sm rounded-md p-3 border"
          style={{
            backgroundColor: "var(--danger-bg)",
            borderColor: "var(--danger)",
            color: "var(--danger-text)",
          }}
        >
          {error}
        </div>
      )}
      {successMsg && (
        <div
          className="text-sm rounded-md p-3 border"
          style={{
            backgroundColor: "var(--success-bg)",
            borderColor: "var(--success)",
            color: "var(--success-text)",
          }}
        >
          {successMsg}
        </div>
      )}

      <section
        className="rounded-lg border p-4"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
      >
        <div className="flex flex-wrap items-center gap-2">
          <button
            type="button"
            onClick={addStep}
            className="px-3 py-1.5 text-sm rounded-md"
            style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
          >
            + Step
          </button>
          <button
            type="button"
            onClick={removeStep}
            className="px-3 py-1.5 text-sm rounded-md"
            style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
          >
            - Step
          </button>
          <span className="mx-1 text-sm" style={{ color: "var(--border-primary)" }}>|</span>
          <button
            type="button"
            onClick={addNote}
            disabled={noteCount >= MAX_NOTES}
            className="px-3 py-1.5 text-sm rounded-md disabled:opacity-50"
            style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
          >
            + Note
          </button>
          <button
            type="button"
            onClick={removeNote}
            disabled={noteCount <= 1}
            className="px-3 py-1.5 text-sm rounded-md disabled:opacity-50"
            style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
          >
            - Note
          </button>
          <span className="mx-1 text-sm" style={{ color: "var(--border-primary)" }}>|</span>
          <button
            type="button"
            onClick={clearAll}
            className="px-3 py-1.5 text-sm rounded-md"
            style={{ backgroundColor: "var(--danger-btn)", color: "var(--text-white)" }}
          >
            Clear
          </button>

          <div className="ml-auto text-xs" style={{ color: "var(--text-muted)" }}>
            {steps.length} steps, {noteCount} notes
          </div>
        </div>
      </section>

      <section
        className="rounded-lg border p-4"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
      >
        <div className="grid grid-cols-1 xl:grid-cols-12 gap-4 items-end">
          <div className="xl:col-span-3">
            <div className="flex items-center gap-2">
              <label className="inline-flex items-center gap-2 text-sm" style={{ color: "var(--text-secondary)" }}>
                <input
                  type="checkbox"
                  checked={loopEnabled}
                  onChange={(e) => setLoopEnabled(e.target.checked)}
                />
                Loop
              </label>
              {!isPlaying ? (
                <button
                  type="button"
                  onClick={handlePlay}
                  className="px-4 py-2 rounded-md text-sm"
                  style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
                >
                  Play
                </button>
              ) : (
                <button
                  type="button"
                  onClick={stopPlayback}
                  className="px-4 py-2 rounded-md text-sm"
                  style={{ backgroundColor: "var(--danger-btn)", color: "var(--text-white)" }}
                >
                  Stop
                </button>
              )}
              <span className="mx-1 text-sm" style={{ color: "var(--border-primary)" }}>|</span>
              <button
                type="button"
                onClick={openDeployModal}
                className="px-4 py-2 rounded-md text-sm"
                style={{ backgroundColor: "var(--text-link)", color: "var(--text-white)" }}
              >
                Deploy as Archetype
              </button>
            </div>
          </div>

          <div className="xl:col-span-5">
            <div className="flex items-center justify-between">
              <label className="text-sm font-medium" style={{ color: "var(--text-secondary)" }}>
                Step Delay
              </label>
              <span className="text-sm font-semibold" style={{ color: "var(--accent)" }}>
                {stepDelayMs} ms
              </span>
            </div>
            <input
              type="range"
              min="40"
              max="2000"
              step="10"
              value={stepDelayMs}
              onChange={(e) => setStepDelayMs(Number(e.target.value))}
              className="w-full mt-2"
            />
          </div>

          <div className="xl:col-span-4">
            <div className="flex items-center justify-between">
              <label className="text-sm font-medium" style={{ color: "var(--text-secondary)" }}>
                Note Duration
              </label>
              <span className="text-sm font-semibold" style={{ color: "var(--accent)" }}>
                {noteDurationMs} ms
              </span>
            </div>
            <input
              type="range"
              min="20"
              max="500"
              step="10"
              value={noteDurationMs}
              onChange={(e) => setNoteDurationMs(Number(e.target.value))}
              className="w-full mt-2"
            />
          </div>

        </div>

        {currentStep >= 0 && (
          <p className="text-xs mt-3" style={{ color: "var(--text-muted)" }}>
            Playing step {currentStep + 1}/{steps.length}
            {activeBellsInCurrentStep.length ? ` (bells: ${activeBellsInCurrentStep.join(", ")})` : " (silence)"}
          </p>
        )}
      </section>

      <section
        className="rounded-lg border"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
      >
        <div className="overflow-x-auto">
          <table className="min-w-max border-separate border-spacing-0">
            <thead>
              <tr>
                <th
                  className="sticky top-0 left-0 z-30 px-3 py-2 text-xs font-semibold border-b border-r"
                  style={{ backgroundColor: "var(--bg-secondary)", borderColor: "var(--border-primary)", color: "var(--text-secondary)" }}
                >
                  Note \\ Step
                </th>
                {steps.map((_, stepIndex) => {
                  const isCurrent = stepIndex === currentStep;
                  return (
                    <th
                      key={stepIndex}
                      className="sticky top-0 z-20 px-2 py-2 text-xs font-semibold border-b border-r"
                      style={{
                        minWidth: "44px",
                        backgroundColor: isCurrent ? "rgba(116,184,22,0.24)" : "var(--bg-secondary)",
                        borderColor: "var(--border-primary)",
                        color: isCurrent ? "var(--accent)" : "var(--text-secondary)",
                      }}
                    >
                      {stepIndex + 1}
                    </th>
                  );
                })}
              </tr>
            </thead>
            <tbody>
              {Array.from({ length: noteCount }, (_, noteIndex) => (
                <tr key={noteIndex}>
                  <th
                    className="sticky left-0 z-10 px-3 py-2 text-xs font-medium border-b border-r"
                    style={{
                      backgroundColor: "var(--bg-secondary)",
                      borderColor: "var(--border-primary)",
                      color: "var(--text-secondary)",
                    }}
                  >
                    {NOTE_LABELS[noteIndex]}
                  </th>
                  {steps.map((stepValue, stepIndex) => {
                    const enabled = Boolean(stepValue & (1 << noteIndex));
                    const isCurrent = stepIndex === currentStep;
                    return (
                      <td
                        key={`${noteIndex}-${stepIndex}`}
                        className="border-b border-r"
                        style={{
                          borderColor: "var(--border-primary)",
                          backgroundColor: isCurrent ? "rgba(116,184,22,0.08)" : "transparent",
                          width: "44px",
                          height: "44px",
                        }}
                      >
                        <button
                          type="button"
                          aria-label={`Toggle note ${noteIndex + 1} on step ${stepIndex + 1}`}
                          aria-pressed={enabled}
                          onClick={() => toggleCell(noteIndex, stepIndex)}
                          className="w-full h-full flex items-center justify-center transition-colors"
                          style={{
                            backgroundColor: "transparent",
                            border: "none",
                            outline: "none",
                          }}
                        >
                          <span
                            aria-hidden="true"
                            style={{
                              width: "64%",
                              height: "64%",
                              borderRadius: "9999px",
                              backgroundColor: "var(--btn-primary)",
                              opacity: enabled ? 1 : 0,
                              transform: enabled ? "scale(1)" : "scale(0.4)",
                              boxShadow: enabled ? "0 0 12px 4px rgba(116, 184, 22, 0.55)" : "none",
                              transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease",
                            }}
                          />
                        </button>
                      </td>
                    );
                  })}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </section>

      <section
        className="rounded-lg border p-4"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
      >
        <h2 className="text-sm font-semibold mb-2" style={{ color: "var(--text-heading)" }}>
          Generated Output Preview
        </h2>
        <div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
          <div>
            <p className="text-xs mb-1" style={{ color: "var(--text-muted)" }}>CSV-like notation</p>
            <pre
              className="rounded-md p-3 text-xs overflow-auto"
              style={{ backgroundColor: "var(--bg-primary)", border: "1px solid var(--border-primary)", color: "var(--text-primary)" }}
            >
{`{${steps.map(stepToNotation).join(",")}}`}
            </pre>
          </div>
          <div>
            <p className="text-xs mb-1" style={{ color: "var(--text-muted)" }}>PROGMEM values</p>
            <pre
              className="rounded-md p-3 text-xs overflow-auto"
              style={{ backgroundColor: "var(--bg-primary)", border: "1px solid var(--border-primary)", color: "var(--text-primary)" }}
            >
{`const uint16_t PROGMEM melody_builtin_custom[] = {\n  ${steps.map(stepToHex).join(", ")}\n};`}
            </pre>
          </div>
        </div>
      </section>

      {showDeployModal && (
        <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 && closeDeployModal()}
        >
          <div
            className="w-full max-w-md rounded-lg border shadow-xl"
            style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
          >
            <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)" }}>
                  Deploy as Archetype
                </h2>
                <p className="text-xs mt-0.5" style={{ color: "var(--text-muted)" }}>
                  Create a new archetype from this composer pattern.
                </p>
              </div>
              <button
                type="button"
                onClick={closeDeployModal}
                className="text-xl leading-none"
                style={{ color: "var(--text-muted)" }}
                disabled={deploying}
              >
                &times;
              </button>
            </div>

            <div className="px-6 py-5 space-y-4">
              {deployError && (
                <div
                  className="text-sm rounded-md p-3 border"
                  style={{
                    backgroundColor: "var(--danger-bg)",
                    borderColor: "var(--danger)",
                    color: "var(--danger-text)",
                  }}
                >
                  {deployError}
                </div>
              )}
              <div>
                <label className="block text-sm font-medium mb-1" style={{ color: "var(--text-secondary)" }}>
                  Name *
                </label>
                <input
                  type="text"
                  value={deployName}
                  onChange={(e) => setDeployName(e.target.value)}
                  className="w-full px-3 py-2 rounded-md text-sm border"
                  placeholder="e.g. Doksologia_3k"
                  disabled={deploying}
                />
              </div>
              <div>
                <label className="block text-sm font-medium mb-1" style={{ color: "var(--text-secondary)" }}>
                  PID *
                </label>
                <input
                  type="text"
                  value={deployPid}
                  onChange={(e) => setDeployPid(e.target.value)}
                  className="w-full px-3 py-2 rounded-md text-sm border"
                  placeholder="e.g. builtin_doksologia_3k"
                  disabled={deploying}
                />
              </div>
            </div>

            <div
              className="flex justify-end gap-2 px-6 py-4 border-t"
              style={{ borderColor: "var(--border-primary)" }}
            >
              <button
                type="button"
                onClick={closeDeployModal}
                className="px-4 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}
                disabled={deploying}
              >
                Cancel
              </button>
              <button
                type="button"
                onClick={handleDeploy}
                className="px-4 py-2 text-sm rounded-md transition-colors disabled:opacity-50"
                style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
                disabled={deploying}
              >
                {deploying ? "Deploying..." : "Deploy"}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
