import { CircularProgress } from "@mui/joy";
import React, {
  forwardRef,
  useImperativeHandle,
  useEffect,
  useRef,
  useState,
  useCallback,
} from "react";
import type { RefObject } from "react";
import { toast, type Id } from 'react-toastify';
import DOMPurify from 'isomorphic-dompurify';
import Box from '@mui/joy/Box';
import Button from "@mui/joy/Button";
import IconButton from "@mui/joy/IconButton";
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import LinkIcon from '@mui/icons-material/Link';
import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline';
import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded';
import { AutoComplete } from 'primereact/autocomplete/autocomplete.esm.js';
import { API_URL } from '../config';
import { useAutoCompleteScrollFix } from '@/hooks/useAutoCompleteScrollFix';

// Type definitions
interface YouTubeVideo {
  video_id: string;
  title: string;
  channel: string;
  duration: string;
  thumbnail?: string | null;
}

interface LyricsResult {
  lyrics: string;
  artist: string;
  song: string;
  source?: string;
  src?: string;
  [key: string]: unknown;
}

interface UICheckboxProps {
  id: string;
  label: string;
  value?: string;
  onToggle?: (source: string) => void;
}

interface UICheckboxHandle {
  setChecked: (val: boolean) => void;
  checked: boolean;
}

interface Suggestion {
  label: string;
  value: string;
  artist?: string;
  song?: string;
}

interface LyricSearchInputFieldProps {
  id: string;
  placeholder: string;
  setShowLyrics: (show: boolean) => void;
}

// Sanitize HTML from external sources to prevent XSS
const sanitizeHtml = (html) => {
  if (!html) return '';
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['br', 'p', 'b', 'i', 'em', 'strong', 'span', 'div'],
    ALLOWED_ATTR: ['class'],
  });
};

export default function LyricSearch() {
  const [showLyrics, setShowLyrics] = useState(false);

  return (
    <div className="lyric-search w-full">
      {/* Hero section */}
      <div className="mt-8 mb-12 text-center flex flex-col items-center">
        <div className="relative w-32 h-32 flex items-center justify-center mb-4">
          <div
            className="absolute inset-0 rounded-full"
            style={{
              background: 'radial-gradient(circle at 50% 50%, rgba(168,85,247,0.25) 0%, rgba(168,85,247,0.15) 30%, rgba(236,72,153,0.08) 60%, transparent 80%)',
            }}
          ></div>
          <span className="relative text-6xl" style={{ marginTop: '-4px' }}>🎤</span>
        </div>
        <h1 className="text-4xl font-bold mb-3 text-neutral-900 dark:text-white tracking-tight">
          Lyric Search
        </h1>
        <p className="text-neutral-600 dark:text-neutral-400 text-base max-w-sm leading-relaxed">
          Search millions of songs instantly.<br />
          <span className="text-neutral-400 dark:text-neutral-500 text-sm">Powered by Genius, LRCLib & more</span>
        </p>
      </div>
      <LyricSearchInputField
        id="lyric-search-input"
        placeholder="Artist - Song"
        setShowLyrics={setShowLyrics}
      />
    </div>
  );
}



export function LyricSearchInputField({ id, placeholder, setShowLyrics }: LyricSearchInputFieldProps) {
  const [value, setValue] = useState("");
  const [suggestions, setSuggestions] = useState<Suggestion[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [excludedSources, setExcludedSources] = useState<string[]>([]);
  const [lyricsResult, setLyricsResult] = useState<LyricsResult | null>(null);
  const [textSize, setTextSize] = useState("normal");
  const [inputStatus, setInputStatus] = useState("hint");
  const [youtubeVideo, setYoutubeVideo] = useState<YouTubeVideo | null>(null);
  const [youtubeLoading, setYoutubeLoading] = useState(false);
  const [highlightedVerse, setHighlightedVerse] = useState<string | null>(null);
  const [isLyricsVisible, setIsLyricsVisible] = useState(false);
  const searchToastRef = useRef<Id | null>(null);
  const autoCompleteRef = useRef<any>(null);
  const autoCompleteInputRef = useRef<HTMLInputElement | null>(null);
  const searchButtonRef = useRef<HTMLButtonElement | null>(null);
  const [theme, setTheme] = useState(document.documentElement.getAttribute("data-theme") || "light");
  const { attachScrollFix: handlePanelShow, cleanupScrollFix: handlePanelHide } = useAutoCompleteScrollFix();
  const statusLabels = {
    hint: "Format: Artist - Song",
    error: "Artist and song required",
    ready: "Format looks good",
  };
  const statusIcons = {
    hint: RemoveRoundedIcon,
    error: CloseRoundedIcon,
    ready: CheckCircleRoundedIcon,
  };
  const statusColors = {
    hint: "#9ca3af",
    error: "#ef4444",
    ready: "#22c55e",
  };

  // Handle URL hash changes and initial load
  useEffect(() => {
    const handleHashChange = () => {
      const hash = window.location.hash.slice(1); // Remove the # symbol
      if (hash) {
        try {
          const [artist, song] = decodeURIComponent(hash).split('/');
          if (artist && song) {
            setValue(`${artist} - ${song}`);
            handleSearch(`${artist} - ${song}`);
          }
        } catch (e) {
          console.error('Failed to parse URL hash:', e);
        }
      }
    };

    window.addEventListener('hashchange', handleHashChange);
    handleHashChange(); // Handle initial load

    return () => window.removeEventListener('hashchange', handleHashChange);
  }, []);

  useEffect(() => {
    const handler = (e) => {
      const newTheme = e.detail;
      setTheme(newTheme);
    };
    document.addEventListener('set-theme', handler);
    return () => {
      document.removeEventListener('set-theme', handler);
    };
  }, []);



  // Typeahead: fetch suggestions
  const fetchSuggestions = useCallback(async (event) => {
    const query = event.query;
    const res = await fetch(`${API_URL}/typeahead/lyrics`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query }),
    });
    const json = await res.json();
    setSuggestions(json);
  }, []);

  // Fetch YouTube video for the song
  const fetchYouTubeVideo = useCallback(async (artist, song) => {
    setYoutubeLoading(true);
    try {
      const res = await fetch(`${API_URL}/yt/search`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ t: `${artist} - ${song}` }),
      });
      if (!res.ok) {
        setYoutubeVideo(null);
        setYoutubeLoading(false);
        return;
      }
      const data = await res.json();
      if (data?.video_id) {
        setYoutubeVideo({
          video_id: data.video_id,
          title: data.extras?.title || `${artist} - ${song}`,
          channel: data.extras?.channel || '',
          duration: data.extras?.duration || '',
          thumbnail: data.extras?.thumbnails?.[0] || null,
        });
      } else {
        setYoutubeVideo(null);
      }
    } catch (err) {
      console.error('YouTube search failed:', err);
      setYoutubeVideo(null);
    }
    setYoutubeLoading(false);
  }, []);

  // Toggle exclusion state for checkboxes
  const toggleExclusion = (source: string) => {
    const lower = source.toLowerCase();
    setExcludedSources((prev) =>
      prev.includes(lower)
        ? prev.filter((s) => s !== lower)
        : [...prev, lower]
    );
  };


  

  const evaluateSearchValue = useCallback((searchValue: string, shouldUpdate = true) => {
    const trimmed = searchValue?.trim() || "";
    let status = "hint";

    if (!trimmed) {
      if (shouldUpdate) setInputStatus(status);
      return { status, valid: false };
    }

    if (!trimmed.includes(" - ")) {
      status = "error";
      if (shouldUpdate) setInputStatus(status);
      return { status, valid: false };
    }

    const [artist, song] = trimmed.split(" - ", 2).map((v) => v.trim());
    if (!artist || !song) {
      status = "error";
      if (shouldUpdate) setInputStatus(status);
      return { status, valid: false };
    }

    status = "ready";
    if (shouldUpdate) setInputStatus(status);
    return { status, valid: true, artist, song };
  }, []);

  useEffect(() => {
    evaluateSearchValue(value);
  }, [value, evaluateSearchValue]);

  // Robustly hide autocomplete panel
  const hideAutocompletePanel = useCallback(() => {
    // Method 1: Use PrimeReact API
    if (autoCompleteRef.current?.hide) {
      try {
        autoCompleteRef.current.hide();
      } catch (e) {
        // Ignore
      }
    }

    // Method 2: Clear suggestions to ensure panel hides
    setSuggestions([]);

    // Method 3: Force hide via DOM after a tick (fallback)
    setTimeout(() => {
      const panel = document.querySelector('.p-autocomplete-panel');
      if (panel) {
        (panel as HTMLElement).style.display = 'none';
        (panel as HTMLElement).style.opacity = '0';
        (panel as HTMLElement).style.visibility = 'hidden';
        (panel as HTMLElement).style.pointerEvents = 'none';
      }
    }, 10);
  }, []);

  const handleSearch = async (searchValue = value) => {
    hideAutocompletePanel();
    autoCompleteInputRef.current?.blur(); // blur early so the suggestion panel closes immediately

    const evaluation = evaluateSearchValue(searchValue);
    if (!evaluation?.valid) {
      const message = statusLabels[evaluation?.status || inputStatus] || "Please use Artist - Song";
      if (!toast.isActive("lyrics-validation-error-toast")) {
        toast.error(message, { toastId: "lyrics-validation-error-toast" });
      }
      return;
    }

    const { artist, song } = evaluation;
    setIsLoading(true);
    setLyricsResult(null);
    setYoutubeVideo(null); // Reset YouTube video
    setShowLyrics(false);

    const toastId = "lyrics-searching-toast";
    toast.dismiss(toastId);
    searchToastRef.current = toast.info("Searching...", {
      toastId,
    });

    const startTime = Date.now();
    const dismissSearchToast = () => {
      if (searchToastRef.current) {
        toast.dismiss(searchToastRef.current);
        searchToastRef.current = null;
      }
    };

    try {
      const res = await fetch(`${API_URL}/lyric/search`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          a: artist,
          s: song,
          excluded_sources: excludedSources,
          src: "Web",
          extra: true,
        }),
      });

      if (res.status === 429) {
        throw new Error("Too many requests. Please wait a moment and try again.");
      }

      const data = await res.json();
      if (!res.ok || !data.lyrics) {
        throw new Error(data.errorText || "Unknown error.");
      }

      const duration = ((Date.now() - startTime) / 1000).toFixed(1);
      setTextSize("normal");
      setLyricsResult({ artist: data.artist, song: data.song, lyrics: data.lyrics, src: data.src });
      setHighlightedVerse(null);
      setIsLyricsVisible(false);
      // Trigger fade-in animation
      requestAnimationFrame(() => {
        requestAnimationFrame(() => setIsLyricsVisible(true));
      });
      setShowLyrics(true);

      // Update URL hash with search parameters
      const hash = `#${encodeURIComponent(data.artist)}/${encodeURIComponent(data.song)}`;
      window.history.pushState(null, '', hash);

      // Search for YouTube video (don't block on this)
      fetchYouTubeVideo(data.artist, data.song);

      dismissSearchToast();
      toast.success(`Found! (Took ${duration}s)`, {
        autoClose: 2500,
        toastId: "lyrics-success-toast",
      });
    } catch (error: unknown) {
      dismissSearchToast();
      toast.error((error as Error)?.message || 'An error occurred', {
        icon: () => "😕",
        autoClose: 5000,
        toastId: "lyrics-error-toast",
      });
    } finally {
      setIsLoading(false);
      hideAutocompletePanel();
      autoCompleteInputRef.current?.blur();
      searchButtonRef.current?.blur();
      searchToastRef.current = null;
    }
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Enter") {
      e.preventDefault();
      hideAutocompletePanel();
      handleSearch();
    }
  };

  const handleCopyLyrics = useCallback(async () => {
    if (!lyricsResult?.lyrics) {
      return;
    }
    const temp = document.createElement("div");
    temp.innerHTML = lyricsResult.lyrics
      .replace(/<br\s*\/?>(\s*<br\s*\/?>)?/gi, (_match, doubleBreak) => {
        return doubleBreak ? "\n\n" : "\n";
      })
      .replace(/<p\b[^>]*>/gi, "")
      .replace(/<\/p>/gi, "\n\n");
    const plainText = temp.textContent || temp.innerText || "";

    try {
      await navigator.clipboard.writeText(plainText);
      toast.success("Lyrics copied to clipboard", { autoClose: 1500 });
    } catch (err) {
      toast.error("Unable to copy lyrics");
      console.error("Copy failed", err);
    }
  }, [lyricsResult]);

  const handleCopyLink = useCallback(async () => {
    if (!lyricsResult) {
      return;
    }
    try {
      const url = new URL(window.location.href);
      const hash = `#${encodeURIComponent(lyricsResult.artist)}/${encodeURIComponent(lyricsResult.song)}`;
      url.hash = hash;
      await navigator.clipboard.writeText(url.toString());
      toast.success("Lyric link copied", { autoClose: 1500 });
    } catch (err) {
      toast.error("Unable to copy link");
      console.error("Link copy failed", err);
    }
  }, [lyricsResult]);

  const statusTitle = statusLabels[inputStatus];
  const StatusIcon = statusIcons[inputStatus] || RemoveRoundedIcon;

  useEffect(() => {
    const inputEl = autoCompleteInputRef.current;
    if (!inputEl) return;
    if (statusTitle) {
      inputEl.setAttribute("title", statusTitle);
    } else {
      inputEl.removeAttribute("title");
    }
  }, [statusTitle]);

  return (
    <div className="w-full">
      <div className="lyric-search-input-wrapper">
        <AutoComplete
          id={id}
          ref={autoCompleteRef}
          value={value}
          suggestions={suggestions}
          completeMethod={fetchSuggestions}
          onChange={(e) => setValue(e.target.value)}
          onKeyDown={handleKeyDown}
          onShow={handlePanelShow}
          onHide={handlePanelHide}
          placeholder={placeholder}
          autoFocus
          style={{ width: '100%' }}
          className={`lyric-search-input ${inputStatus === "error" ? "has-error" : ""} ${inputStatus === "ready" ? "has-ready" : ""}`}
          aria-invalid={inputStatus === "error"}
          aria-label={`Lyric search input. ${statusTitle}`}
          inputRef={(el) => {
            autoCompleteInputRef.current = el;
          }}
          aria-controls="lyric-search-input"
        />
        <span
          className={`input-status-icon input-status-icon-${inputStatus}`}
          title={statusTitle}
          aria-hidden="true"
          style={{ opacity: inputStatus === "hint" ? 0.4 : 1 }}
        >
          <StatusIcon fontSize="small" htmlColor={statusColors[inputStatus]} />
        </span>
        <span className="sr-only" aria-live="polite">
          {statusTitle}
        </span>
      </div>
      <div className="flex flex-wrap items-center justify-center gap-4 mt-5 mb-8">
        <Button
          onClick={() => handleSearch()}
          className="search-btn"
          ref={searchButtonRef}
        >
          Search
        </Button>
        <div className="h-6 w-px bg-neutral-300 dark:bg-neutral-700 hidden sm:block" aria-hidden="true"></div>
        <div className="exclude-sources">
          <span className="exclude-label hidden sm:inline">Exclude:</span>
          <UICheckbox id="excl-Genius" label="Genius" value="Genius" onToggle={toggleExclusion} />
          <UICheckbox id="excl-LRCLib-Cache" label="LRCLib" value="LRCLib-Cache" onToggle={toggleExclusion} />
          <UICheckbox id="excl-Cache" label="Cache" value="Cache" onToggle={toggleExclusion} />
        </div>
      </div>

      {isLoading && (
        <div className="mt-6 flex justify-center">
          <CircularProgress variant="plain" color="primary" size="md" />
        </div>
      )}

      {lyricsResult && (
        <div className={`lyrics-card lyrics-card-${theme} mt-6 p-5 rounded-xl shadow-lg lyrics-card-animate ${isLyricsVisible ? 'lyrics-card-visible' : ''}`}>
          <div className="lyrics-toolbar">
            <div className="lyrics-title" title={`${lyricsResult.artist} - ${lyricsResult.song}`}>
              {lyricsResult.artist} - {lyricsResult.song}
            </div>
            <div className="lyrics-actions">
              {lyricsResult.src && (() => {
                let sourceLabel, sourceClass;
                const src = lyricsResult.src.toLowerCase();
                if (src.includes('redis cache')) {
                  sourceLabel = 'Cache';
                  sourceClass = 'source-pill--cache';
                } else if (src.includes('genius')) {
                  sourceLabel = 'Genius';
                  sourceClass = 'source-pill--genius';
                } else if (src.includes('lrclib')) {
                  sourceLabel = 'LRCLib';
                  sourceClass = 'source-pill--lrclib';
                } else {
                  sourceLabel = lyricsResult.src.split(' ')[0];
                  sourceClass = 'source-pill--other';
                }
                return <span className={`source-pill ${sourceClass}`}>{sourceLabel}</span>;
              })()}
              <div className="text-size-buttons" aria-label="Lyric text size">
                <button
                  type="button"
                  className={`text-size-btn text-size-large ${textSize === "large" ? "active" : ""}`}
                  onClick={() => setTextSize("large")}
                  title="Larger text"
                >
                  Aa
                </button>
                <button
                  type="button"
                  className={`text-size-btn ${textSize === "normal" ? "active" : ""}`}
                  onClick={() => setTextSize("normal")}
                  title="Default text"
                >
                  Aa
                </button>
              </div>
              <IconButton
                size="sm"
                variant="plain"
                color="neutral"
                aria-label="Copy lyrics"
                title="Copy lyrics"
                className="lyrics-action-button"
                onClick={handleCopyLyrics}
              >
                <ContentCopyIcon fontSize="small" />
              </IconButton>
              <IconButton
                size="sm"
                variant="plain"
                color="neutral"
                aria-label="Copy lyric link"
                title="Copy shareable link"
                className="lyrics-action-button"
                onClick={handleCopyLink}
              >
                <LinkIcon fontSize="small" />
              </IconButton>
              {youtubeLoading && (
                <IconButton
                  size="sm"
                  variant="plain"
                  color="neutral"
                  aria-label="Loading YouTube video..."
                  title="Finding video..."
                  className="lyrics-action-button"
                  disabled
                  sx={{ opacity: 0.3 }}
                >
                  <PlayCircleOutlineIcon fontSize="small" />
                </IconButton>
              )}
              {!youtubeLoading && youtubeVideo && (
                <IconButton
                  size="sm"
                  variant="plain"
                  color="neutral"
                  aria-label="Watch on YouTube"
                  title={`Watch: ${youtubeVideo.title}`}
                  className="lyrics-action-button"
                  component="a"
                  href={`https://www.youtube.com/watch?v=${youtubeVideo.video_id}`}
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  <PlayCircleOutlineIcon fontSize="small" />
                </IconButton>
              )}
            </div>
          </div>
          <div className={`lyrics-content ${textSize === "large" ? "lyrics-content-large" : ""}`}>
            <div dangerouslySetInnerHTML={{ __html: sanitizeHtml(lyricsResult.lyrics) }} />
          </div>
        </div>
      )}
    </div>
  );
}


export const UICheckbox = forwardRef<UICheckboxHandle, UICheckboxProps>(function UICheckbox(props, ref) {
  const [checked, setChecked] = useState(false);

  useImperativeHandle(ref, () => ({
    setChecked: (val) => setChecked(val),
    checked,
  }));

  const verifyExclusions = () => {
    const checkboxes = document.querySelectorAll(".exclude-chip") as NodeListOf<HTMLElement>;
    const checkedCount = [...checkboxes].filter(cb => cb.dataset.checked === 'true').length;

    if (checkedCount === 3) {
      // Reset all by triggering clicks
      checkboxes.forEach(cb => cb.click());
      if (!toast.isActive("lyrics-exclusion-reset-toast")) {
        toast.error("All sources were excluded; exclusions have been reset.",
          { toastId: "lyrics-exclusion-reset-toast" }
        );
      }
    }
  };

  const handleClick = () => {
    const newChecked = !checked;
    setChecked(newChecked);
    if (props.onToggle) {
      const source = props.value || props.label;
      props.onToggle(source);
    }
    setTimeout(verifyExclusions, 0);
  };

  return (
    <button
      type="button"
      id={props.id}
      className={`exclude-chip ${checked ? 'exclude-chip--active' : ''}`}
      data-checked={checked}
      onClick={handleClick}
      aria-pressed={checked}
    >
      {props.label}
    </button>
  );
});


interface LyricResultBoxOpts {
  theme?: string;
}

export function LyricResultBox(opts: LyricResultBoxOpts = {}) {
  const theme = opts.theme || 'dark';
  return (
    <div>
      <Box className={`lyrics-card lyrics-card-${theme}`} sx={{ p: 2 }}>
        <div className='lyrics-content'></div>
        {/* <ContentCopyIcon className='lyrics-card-copyButton' size='lg' /> */}
      </Box>
    </div>
  )
}