import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faYoutube } from "@fortawesome/free-brands-svg-icons";
import { faLightbulb, faMoon, faHistory, faCog, faRobot } from "@fortawesome/free-solid-svg-icons";
import { Link } from "react-router-dom";
import { InputPanel } from "../components/InputPanel";
import type { InputValues } from "../components/InputPanel";
import {
  ResultsPanel,
  type RegeneratingMap,
  type SectionKey,
} from "../components/ResultsPanel";
import { StickyZipBar } from "../components/StickyZipBar";
import { HistoryDrawer } from "../components/HistoryPanel";
import { Footer } from "../components/Footer";
import { useToast } from "../lib/toast";
import { generateSong, getServerStatus } from "../lib/llm";
import { addToHistory, type HistoryEntry } from "../lib/history";
import { buildSongZip, downloadBlob, sanitizeFilename } from "../lib/zip";
import {
  SECTION_LABELS,
  type SongAssets,
  type VideoPrompt,
} from "../lib/types";
import { useI18n } from "../lib/i18n";
import { useTheme } from "../lib/theme";

const DRAFT_KEY = "melodymuse-draft";
const DRAFT_DEBOUNCE_MS = 400;

const DEFAULT_INPUT: InputValues = {
  idea: "",
  language: "English",
  customLanguage: "",
  style_hint: "",
  mood: "",
  vocals: "vocals",
};

function resolveLanguage(v: InputValues): string {
  return v.language === "Other"
    ? v.customLanguage.trim() || "English"
    : v.language;
}

function isAnyBusy(loading: boolean, regenerating: RegeneratingMap): boolean {
  if (loading) return true;
  for (const v of Object.values(regenerating)) if (v) return true;
  return false;
}

function loadDraft(): InputValues | null {
  if (typeof window === "undefined") return null;
  try {
    const raw = window.localStorage.getItem(DRAFT_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw) as Partial<InputValues> | null;
    if (!parsed || typeof parsed !== "object") return null;
    return { ...DEFAULT_INPUT, ...parsed };
  } catch {
    return null;
  }
}

function FlagDE() {
  return (
    <svg viewBox="0 0 5 3" className="w-5 h-3.5 rounded-[2px] overflow-hidden shadow-sm">
      <rect width="5" height="3" y="0" fill="#000" />
      <rect width="5" height="2" y="1" fill="#D00" />
      <rect width="5" height="1" y="2" fill="#FFCE00" />
    </svg>
  );
}

function FlagEN() {
  return (
    <svg viewBox="0 0 60 30" className="w-5 h-3.5 rounded-[2px] overflow-hidden shadow-sm">
      <clipPath id="t"><path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/></clipPath>
      <rect width="60" height="30" fill="#012169"/>
      <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" strokeWidth="6"/>
      <path d="M0,0 L60,30 M60,0 L0,30" clipPath="url(#t)" stroke="#C8102E" strokeWidth="4"/>
      <path d="M30,0 v30 M0,15 h60" stroke="#fff" strokeWidth="10"/>
      <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" strokeWidth="6"/>
    </svg>
  );
}

/** Animated equalizer bars for the logo */
function MusicBarsLogo() {
  return (
    <div className="flex items-end gap-[3px] h-[22px]" aria-hidden>
      <div className="music-bar music-bar-1" style={{ height: 12 }} />
      <div className="music-bar music-bar-2" style={{ height: 22 }} />
      <div className="music-bar music-bar-3" style={{ height: 10 }} />
      <div className="music-bar music-bar-4" style={{ height: 16 }} />
      <div className="music-bar music-bar-5" style={{ height: 8 }} />
    </div>
  );
}

export function HomePage() {
  const toast = useToast();
  const { t, lang, setLang } = useI18n();
  const { theme, toggleTheme } = useTheme();

  const [input, setInput] = useState<InputValues>(
    () => loadDraft() ?? DEFAULT_INPUT,
  );
  const [assets, setAssets] = useState<SongAssets | null>(null);
  const [originalAssets, setOriginalAssets] = useState<SongAssets | null>(null);
  const [loading, setLoading] = useState(false);
  const [regenerating, setRegenerating] = useState<RegeneratingMap>({});
  const [selectedTitleIndex, setSelectedTitleIndex] = useState(0);
  const [customTitle, setCustomTitle] = useState<string | null>(null);
  const [serverOk, setServerOk] = useState<boolean | null>(null);
  const [historyOpen, setHistoryOpen] = useState(false);
  const [chainRegenerate, setChainRegenerate] = useState<SectionKey | null>(null);

  const abortRef = useRef<AbortController | null>(null);
  const anyRegenerating = isAnyBusy(loading, regenerating);

  // Save draft
  useEffect(() => {
    const id = window.setTimeout(() => {
      try { window.localStorage.setItem(DRAFT_KEY, JSON.stringify(input)); }
      catch { /* ignore */ }
    }, DRAFT_DEBOUNCE_MS);
    return () => window.clearTimeout(id);
  }, [input]);

  // Server health check
  useEffect(() => {
    let cancelled = false;
    const check = async () => {
      try {
        const s = await getServerStatus();
        if (!cancelled) setServerOk(Boolean(s.ok));
      } catch {
        if (!cancelled) setServerOk(false);
      }
    };
    check();
    const id = window.setInterval(check, 30_000);
    return () => { cancelled = true; window.clearInterval(id); };
  }, []);

  useEffect(() => () => abortRef.current?.abort(), []);

  // Resolved title for ZIP: custom override > chip selection
  const selectedTitle = useMemo(() => {
    if (customTitle !== null) return customTitle;
    if (!assets || assets.titles.length === 0) return undefined;
    return assets.titles[Math.min(selectedTitleIndex, assets.titles.length - 1)];
  }, [assets, selectedTitleIndex, customTitle]);

  const cancel = useCallback(() => {
    abortRef.current?.abort();
    abortRef.current = null;
  }, []);

  useEffect(() => {
    if (!anyRegenerating) return;
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") { e.preventDefault(); cancel(); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [anyRegenerating, cancel]);

  const buildRequest = useCallback(
    (section: string, extra: Record<string, unknown> = {}): GenerateCall => ({
      input: input.idea.trim(),
      language: resolveLanguage(input),
      ...(input.mood.trim() ? { mood: input.mood.trim() } : {}),
      ...(input.style_hint.trim() ? { style_hint: input.style_hint.trim() } : {}),
      vocals: input.vocals,
      section,
      ...extra,
    } as unknown as GenerateCall),
    [input],
  );

  const runGeneration = useCallback(
    async (
      build: () => GenerateCall,
      onSuccess: (result: Partial<SongAssets>) => void,
      busySetter: (b: boolean) => void,
      successMsg: string,
    ) => {
      abortRef.current?.abort();
      const ctrl = new AbortController();
      abortRef.current = ctrl;

      busySetter(true);
      try {
        const req = build();
        const result = await generateSong(req, { signal: ctrl.signal });
        onSuccess(result);
        toast.success(successMsg);
      } catch (err) {
        if (err instanceof DOMException && err.name === "AbortError") {
          toast.info(t.generationCancelled);
        } else {
          toast.error(err instanceof Error ? err.message : t.generationFailed);
        }
      } finally {
        busySetter(false);
        if (abortRef.current === ctrl) abortRef.current = null;
      }
    },
    [toast, t],
  );

  const handleGenerate = useCallback(async () => {
    if (!input.idea.trim()) return;
    setAssets(null);
    setOriginalAssets(null);
    setCustomTitle(null);
    await runGeneration(
      () => buildRequest("all"),
      (result) => {
        const next = result as SongAssets;
        setAssets(next);
        setOriginalAssets(next);
        setSelectedTitleIndex(0);
        addToHistory(input, next);
      },
      setLoading,
      t.assetsGenerated,
    );
  }, [input, buildRequest, runGeneration, t]);

  const handleRegenerateAll = useCallback(async () => {
    if (!input.idea.trim()) return;
    setAssets(null);
    setOriginalAssets(null);
    setCustomTitle(null);
    await runGeneration(
      () => buildRequest("all"),
      (result) => {
        const next = result as SongAssets;
        setAssets(next);
        setOriginalAssets(next);
        setSelectedTitleIndex(0);
        addToHistory(input, next);
      },
      (b) => {
        setLoading(b);
        setRegenerating((m) => ({ ...m, all: b }));
      },
      t.regeneratedAll,
    );
  }, [input, buildRequest, runGeneration, t]);

  const handleRegenerateSection = useCallback(
    async (section: SectionKey, selectedText?: string) => {
      if (!input.idea.trim() || !assets) return;
      
      const isPartialLyrics = section === "lyrics" && !!selectedText;
      const endpointSection = isPartialLyrics ? "lyrics_partial" : section;
      const reqExtras = { context: assets };
      if (isPartialLyrics) {
        (reqExtras as any).selected_text = selectedText;
      }

      await runGeneration(
        () => buildRequest(endpointSection, reqExtras),
        (result) => {
          let merged: SongAssets;
          if (isPartialLyrics) {
            // Replace only the selected text in the existing lyrics
            let newPartial = (result as any).lyrics || (result as any).text || String(result);
            // Cleanup quotes or markdown if the LLM leaked them
            newPartial = newPartial.replace(/^```[a-z]*\n/gi, '').replace(/\n```$/g, '').trim();
            if (newPartial.startsWith('"') && newPartial.endsWith('"')) {
              newPartial = newPartial.slice(1, -1).trim();
            }
            
            // Normalize newlines to prevent replace() from failing due to \r\n vs \n
            const normalizedAssetsLyrics = assets.lyrics.replace(/\r\n/g, '\n');
            const normalizedSelected = selectedText.replace(/\r\n/g, '\n');
            const replaced = normalizedAssetsLyrics.replace(normalizedSelected, newPartial);
            
            merged = { ...assets, lyrics: replaced };
          } else {
            merged = { ...assets, ...result };
          }
          
          setAssets(merged);
          setOriginalAssets((prev) => (prev ? { ...prev, ...result } : merged));
          if (section === "titles") setCustomTitle(null);

          // Chain YouTube description regeneration if lyrics changed
          if (section === "lyrics") {
            setChainRegenerate("youtube_description");
          }
        },
        (b) => setRegenerating((m) => ({ ...m, [section]: b })),
        `${t.regeneratedSection} ${SECTION_LABELS[section]}`,
      );
    },
    [input, assets, buildRequest, runGeneration, t],
  );

  // Execute chained regeneration
  useEffect(() => {
    if (chainRegenerate && assets && !anyRegenerating) {
      const section = chainRegenerate;
      setChainRegenerate(null);
      handleRegenerateSection(section);
    }
  }, [chainRegenerate, assets, anyRegenerating, handleRegenerateSection]);

  const handleDownload = useCallback(async () => {
    if (!assets || !selectedTitle) return;
    try {
      const blob = await buildSongZip(assets, selectedTitle);
      const safe = sanitizeFilename(selectedTitle);
      downloadBlob(blob, `${safe || "song"}.zip`);
    } catch {
      toast.error("Could not generate ZIP — please try again");
    }
  }, [assets, selectedTitle, toast]);

  const handleAssetChange = useCallback(
    <K extends keyof SongAssets>(key: K, value: SongAssets[K]) => {
      setAssets((curr) => (curr ? { ...curr, [key]: value } : curr));
    },
    [],
  );

  const handleVideoPromptChange = useCallback(
    (index: number, value: string) => {
      setAssets((curr) => {
        if (!curr) return curr;
        const next: VideoPrompt[] = curr.video_prompts.map((p, i) =>
          i === index ? { ...p, prompt: value } : p,
        );
        return { ...curr, video_prompts: next };
      });
    },
    [],
  );

  const handleLoadHistory = useCallback(
    (entry: HistoryEntry) => {
      if (anyRegenerating) cancel();
      setInput(entry.input);
      setAssets(entry.assets);
      setOriginalAssets(entry.assets);
      setSelectedTitleIndex(0);
      setCustomTitle(null);
      toast.info(`Loaded "${truncate(entry.input.idea, 60)}"`);
    },
    [anyRegenerating, cancel, toast],
  );

  return (
    <div className="flex flex-col min-h-[100dvh] md:h-screen overflow-y-auto md:overflow-hidden">
      {/* Liquid background blobs */}
      <div className="liquid-blob-1" />
      <div className="liquid-blob-2" />
      <div className="liquid-blob-3" />

      {/* Header */}
      <header className="shrink-0 z-30 sticky top-0 bg-bg-card/70 backdrop-blur-md border-b border-border shadow-sm">
        <div className="max-w-screen-2xl mx-auto px-4 sm:px-6 h-12 flex items-center justify-between">
          {/* Logo */}
          <div className="flex items-center gap-2.5">
            <MusicBarsLogo />
            <span className="text-sm font-extrabold gradient-text-animated tracking-tight">
              MelodyMuse
            </span>
          </div>

          {/* Right controls */}
          <div className="flex items-center gap-4">
            {/* External Links */}
            <div className="flex items-center gap-1">
              <a
                href="https://suno.com/create"
                target="_blank"
                rel="noopener noreferrer"
                className="px-2 h-8 flex items-center justify-center rounded-lg text-fg-muted hover:text-orange-500 hover:bg-orange-500/10 transition-all duration-150 font-bold text-[11px] tracking-wide uppercase"
                title="Create on Suno"
              >
                Suno
              </a>
              <a
                href="https://chat.orfel.de/c/new"
                target="_blank"
                rel="noopener noreferrer"
                className="w-8 h-8 flex items-center justify-center rounded-lg text-fg-muted hover:text-blue-400 hover:bg-blue-400/10 transition-all duration-150"
                title="Open Chat AI"
              >
                <FontAwesomeIcon icon={faRobot} className="w-4 h-4" />
              </a>
              <a
                href="https://www.youtube.com/@AIWentNonsense"
                target="_blank"
                rel="noopener noreferrer"
                className="w-8 h-8 flex items-center justify-center rounded-lg text-fg-muted hover:text-rose-500 hover:bg-rose-500/10 transition-all duration-150"
                title="AI Went Nonsense on YouTube"
              >
                <FontAwesomeIcon icon={faYoutube} className="w-4 h-4" />
              </a>
            </div>

            {/* Server offline indicator */}
            {serverOk === false && (
              <span className="text-[10px] font-bold text-rose-400 bg-rose-400/10 px-2 py-0.5 rounded-full border border-rose-400/20">
                {t.serverOffline}
              </span>
            )}

            {/* Divider */}
            <div className="h-5 w-px bg-border" />

            {/* Language */}
            <div className="flex items-center gap-2">
              <button
                onClick={() => setLang("de")}
                className={`flex items-center justify-center transition-all duration-150 ${lang === "de" ? "scale-110 ring-2 ring-accent-primary rounded-[4px]" : "opacity-40 hover:opacity-80"}`}
                title="Deutsch"
              >
                <FlagDE />
              </button>
              <button
                onClick={() => setLang("en")}
                className={`flex items-center justify-center transition-all duration-150 ${lang === "en" ? "scale-110 ring-2 ring-accent-primary rounded-[4px]" : "opacity-40 hover:opacity-80"}`}
                title="English"
              >
                <FlagEN />
              </button>
            </div>

            {/* Divider */}
            <div className="h-5 w-px bg-border" />

            {/* History toggle */}
            <button
              onClick={() => setHistoryOpen((o) => !o)}
              className="w-8 h-8 flex items-center justify-center rounded-lg text-fg-muted hover:text-accent-primary hover:bg-bg-hover transition-all duration-150 relative"
              title={t.history}
            >
              <FontAwesomeIcon icon={faHistory} className="w-3.5 h-3.5" />
            </button>

            {/* Settings link */}
            <Link
              to="/settings"
              className="w-8 h-8 flex items-center justify-center rounded-lg text-fg-muted hover:text-accent-primary hover:bg-bg-hover transition-all duration-150"
              title={t.settings}
            >
              <FontAwesomeIcon icon={faCog} className="w-3.5 h-3.5" />
            </Link>

            {/* Theme toggle switch */}
            <button
              onClick={toggleTheme}
              className="relative flex items-center justify-between w-12 h-6 bg-border/50 border border-border/50 hover:bg-border/80 rounded-full p-0.5 cursor-pointer transition-colors"
              title="Toggle dark/light mode"
            >
              <div
                className={`absolute left-0.5 top-0.5 w-5 h-5 rounded-full shadow-sm transition-transform duration-300 ease-in-out z-0 ${
                  theme === "dark" ? "translate-x-6 bg-bg-card ring-1 ring-border" : "translate-x-0 bg-white ring-1 ring-gray-200"
                }`}
              >
                <div className={`w-full h-full rounded-full ${theme === "dark" ? "gradient-bg opacity-10" : "opacity-0"} transition-opacity duration-300`} />
              </div>
              <div className="w-5 h-5 flex items-center justify-center z-10">
                <FontAwesomeIcon icon={faLightbulb} className={`w-2.5 h-2.5 transition-colors ${theme === "dark" ? "text-fg-muted/60" : "text-yellow-500"}`} />
              </div>
              <div className="w-5 h-5 flex items-center justify-center z-10">
                <FontAwesomeIcon icon={faMoon} className={`w-2.5 h-2.5 transition-colors ${theme === "dark" ? "text-accent-primary" : "text-fg-muted/60"}`} />
              </div>
            </button>
          </div>
        </div>
      </header>

      {/* Main content: 1/3 left input + 2/3 right results */}
      <div className="flex-1 min-h-0 flex flex-col md:flex-row md:overflow-hidden max-w-screen-2xl mx-auto w-full px-4 sm:px-6 py-4 gap-5">

        {/* LEFT PANEL — 1/3 — Input */}
        <div className="w-full md:w-[340px] xl:w-[380px] shrink-0 flex flex-col">
          <div className="card p-4 flex flex-col flex-none md:flex-1 min-h-0">
            <InputPanel
              values={input}
              onChange={setInput}
              onSubmit={handleGenerate}
              loading={loading}
              disabled={anyRegenerating}
              cancelButton={
                <button
                  type="button"
                  onClick={cancel}
                  className="btn-secondary w-full text-xs"
                >
                  {t.cancelGeneration}
                </button>
              }
            />
          </div>
        </div>

        {/* RIGHT PANEL — 2/3 — Results */}
        <div className="flex-1 min-w-0 flex flex-col md:overflow-hidden gap-4 md:gap-0 pb-4 md:pb-0">
          <div className="flex-1 md:overflow-y-auto md:scrollbar-thin px-1">
            <ResultsPanel
              assets={assets}
              originalAssets={originalAssets}
              loading={loading}
              selectedTitleIndex={selectedTitleIndex}
              onSelectTitle={setSelectedTitleIndex}
              onTitleEdit={setCustomTitle}
              onChange={handleAssetChange}
              onChangeVideoPrompt={handleVideoPromptChange}
              onRegenerateAll={handleRegenerateAll}
              onRegenerateSection={handleRegenerateSection}
              onCancel={cancel}
              anyRegenerating={anyRegenerating}
              regenerating={regenerating}
            />
          </div>

          {/* Download bar inside results column, always visible */}
          {assets && (
            <div className="shrink-0 pt-2 sticky bottom-4 md:static z-20">
              <StickyZipBar
                title={selectedTitle}
                busy={loading}
                onDownload={handleDownload}
                inline
              />
            </div>
          )}
        </div>
      </div>

      {/* Footer — always visible */}
      <Footer />

      {/* History Drawer (right-side slide-in) */}
      <HistoryDrawer
        open={historyOpen}
        onClose={() => setHistoryOpen(false)}
        onLoad={handleLoadHistory}
      />
    </div>
  );
}

function truncate(s: string, n: number): string {
  const t = s.trim();
  if (t.length <= n) return t || "previous generation";
  return t.slice(0, n - 1) + "…";
}

type GenerateCall = Parameters<typeof generateSong>[0];
