import { useEffect, useMemo, useRef, useState } from "react";

const DEFAULT_API = typeof window === "undefined" ? "http://localhost:8000" : window.location.origin;

function useDebounce(value, delay) {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const handle = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(handle);
  }, [value, delay]);

  return debounced;
}

function Toast({ toast }) {
  if (!toast) return null;
  return (
    <div className={`toast ${toast.type}`}>
      <span>{toast.message}</span>
    </div>
  );
}

function MovieCard({ movie, apiBase, onOpen }) {
  const [imgError, setImgError] = useState(false);
  const thumbUrl = `${apiBase}/thumb/${movie.id}`;
  const showFallback = !movie.has_thumb || imgError;

  return (
    <button className="card" type="button" onClick={() => onOpen(movie)}>
      <div className="thumb">
        {showFallback ? (
          <div className="thumb-fallback">🎞</div>
        ) : (
          <img
            src={thumbUrl}
            alt={movie.title}
            loading="lazy"
            onError={() => setImgError(true)}
          />
        )}
        <div className="thumb-overlay">▶</div>
        <div className="badge">{movie.duration}</div>
      </div>
      <div className="card-body">
        <h3 title={movie.title}>{movie.title}</h3>
        <p>
          {movie.size} · {movie.width || "--"}x{movie.height || "--"}
        </p>
      </div>
    </button>
  );
}

function Modal({ movie, apiBase, onClose }) {
  const videoRef = useRef(null);
  const warningText = useMemo(() => {
    const title = (movie?.title || "").toLowerCase();
    if (!title) return "";
    const hasHevc = title.includes("hevc") || title.includes("x265") || title.includes("h265");
    const has10bit = title.includes("10bit") || title.includes("10-bit") || title.includes("10 bit");
    if (hasHevc || has10bit) {
      return "This file looks like HEVC/x265 or 10-bit. Most browsers cannot play it. Use Open Stream or a compatible file.";
    }
    return "";
  }, [movie]);

  useEffect(() => {
    const handle = (event) => {
      if (event.key === "Escape") {
        onClose();
      }
    };
    window.addEventListener("keydown", handle);
    return () => window.removeEventListener("keydown", handle);
  }, [onClose]);

  const handlePlay = async () => {
    if (!videoRef.current) return;
    const video = videoRef.current;
    const botUrl = `${apiBase}/stream-bot/${movie.id}`;
    const fallbackUrl = `${apiBase}/stream/${movie.id}`;

    const handleError = async () => {
      video.removeEventListener("error", handleError);
      video.src = fallbackUrl;
      try {
        await video.play();
      } catch {
        // ignore autoplay errors
      }
    };

    video.addEventListener("error", handleError, { once: true });
    video.src = botUrl;
    try {
      await video.play();
    } catch {
      // ignore autoplay errors
    }
  };

  const handleStop = () => {
    if (!videoRef.current) return;
    videoRef.current.pause();
    videoRef.current.removeAttribute("src");
    videoRef.current.load();
  };

  const handleOpenStream = () => {
    window.open(`${apiBase}/stream/${movie.id}`, "_blank");
  };

  return (
    <div className="modal-backdrop" onClick={onClose} role="presentation">
      <div className="modal" onClick={(event) => event.stopPropagation()}>
        <button className="close" onClick={onClose} type="button">
          ×
        </button>
        <div className="modal-video">
          <video ref={videoRef} controls />
        </div>
        <div className="modal-meta">
          <h2>{movie.title}</h2>
          <p>
            {movie.duration} · {movie.size} · {movie.width || "--"}x{movie.height || "--"}
          </p>
          {warningText && <div className="codec-warning">{warningText}</div>}
          <div className="modal-actions">
            <button className="primary" type="button" onClick={handlePlay}>
              Play
            </button>
            <button type="button" onClick={handleStop}>
              Stop
            </button>
            <button type="button" onClick={handleOpenStream}>
              Open Stream
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

export default function App() {
  const [apiUrl, setApiUrl] = useState(DEFAULT_API);
  const [movies, setMovies] = useState([]);
  const [filtered, setFiltered] = useState([]);
  const [loading, setLoading] = useState(false);
  const [loadingMore, setLoadingMore] = useState(false);
  const [error, setError] = useState("");
  const [selected, setSelected] = useState(null);
  const [query, setQuery] = useState("");
  const [toast, setToast] = useState(null);
  const [nextOffsetId, setNextOffsetId] = useState(null);
  const gridRef = useRef(null);

  const debouncedQuery = useDebounce(query, 300);
  const apiBase = useMemo(() => apiUrl.replace(/\/+$/, ""), [apiUrl]);

  useEffect(() => {
    if (!toast) return undefined;
    const handle = setTimeout(() => setToast(null), 3000);
    return () => clearTimeout(handle);
  }, [toast]);

  useEffect(() => {
    if (!gridRef.current) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && nextOffsetId && !loadingMore && !loading) {
          loadMore();
        }
      },
      { threshold: 0.1 }
    );

    const sentinel = gridRef.current.querySelector(".load-more-sentinel");
    if (sentinel) {
      observer.observe(sentinel);
    }

    return () => observer.disconnect();
  }, [nextOffsetId, loadingMore, loading]);

  const showToast = (message, type) => {
    setToast({ message, type });
  };

  const fetchMovies = async (refresh = false, appendMode = false) => {
    if (appendMode) {
      setLoadingMore(true);
    } else {
      setLoading(true);
      setNextOffsetId(null);
    }
    setError("");
    try {
      const offset = appendMode && nextOffsetId ? `&offset_id=${nextOffsetId}` : "";
      const response = await fetch(`${apiBase}/movies?refresh=${refresh}&limit=500${offset}`);
      if (!response.ok) {
        throw new Error("Failed to fetch movies");
      }
      const data = await response.json();
      const items = Array.isArray(data) ? data : data.items || [];
      
      if (appendMode) {
        setMovies((prev) => [...prev, ...items]);
      } else {
        setMovies(items);
        setFiltered(items);
        showToast("Movies loaded", "success");
      }
      
      setNextOffsetId(data.next_offset_id || null);
    } catch (err) {
      const message = err instanceof Error ? err.message : "Unable to load movies";
      setError(message);
      showToast(message, "error");
    } finally {
      if (appendMode) {
        setLoadingMore(false);
      } else {
        setLoading(false);
      }
    }
  };

  const loadMore = async () => {
    if (!nextOffsetId) return;
    await fetchMovies(false, true);
  };

  useEffect(() => {
    if (!debouncedQuery) {
      setFiltered(movies);
      return;
    }
    const lower = debouncedQuery.toLowerCase();
    const local = movies.filter((movie) => movie.title.toLowerCase().includes(lower));
    setFiltered(local);

    if (local.length === 0 && movies.length > 0) {
      fetch(`${apiBase}/search?q=${encodeURIComponent(debouncedQuery)}`)
        .then((res) => (res.ok ? res.json() : []))
        .then((data) => {
          const items = Array.isArray(data) ? data : data.items || [];
          setFiltered(items);
        })
        .catch(() => setFiltered([]));
    }
  }, [debouncedQuery, movies, apiBase]);

  const statsLabel = debouncedQuery
    ? `${filtered.length} results for '${debouncedQuery}'`
    : `${movies.length} movies${nextOffsetId ? " (scroll to load more)" : ""}`;

  return (
    <div className="app">
      <style>{`
        @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap');

        :root {
          color-scheme: dark;
          --bg: #0b0b0f;
          --surface: #13131a;
          --surface-2: #181823;
          --accent: #e8b84b;
          --muted: #7a7a8c;
          --text: #f5f5f7;
          --danger: #ef4444;
          --success: #10b981;
        }

        * {
          box-sizing: border-box;
          font-family: 'DM Sans', system-ui, sans-serif;
        }

        body {
          margin: 0;
          background: radial-gradient(circle at top, #1a1a27 0%, #0b0b0f 50%);
          color: var(--text);
          min-height: 100vh;
        }

        #root {
          min-height: 100vh;
        }

        .app {
          padding-top: 120px;
          padding-bottom: 48px;
        }

        .config-bar {
          position: fixed;
          top: 0;
          left: 0;
          right: 0;
          display: flex;
          align-items: center;
          gap: 12px;
          padding: 12px 24px;
          background: #0d0d14ee;
          backdrop-filter: blur(12px);
          z-index: 20;
          border-bottom: 1px solid #1f1f2d;
        }

        .config-bar input {
          flex: 1;
          padding: 10px 12px;
          border-radius: 8px;
          border: 1px solid #262636;
          background: #0f0f18;
          color: var(--text);
        }

        .config-bar button,
        .header button,
        .modal-actions button {
          padding: 10px 16px;
          border-radius: 8px;
          border: 1px solid transparent;
          background: var(--surface-2);
          color: var(--text);
          cursor: pointer;
          transition: transform 0.2s ease, background 0.2s ease;
        }

        .config-bar button.primary,
        .header button.primary,
        .modal-actions button.primary {
          background: var(--accent);
          color: #1b1503;
          font-weight: 600;
        }

        .config-bar button:hover,
        .header button:hover,
        .modal-actions button:hover {
          transform: translateY(-1px);
        }

        .header {
          position: sticky;
          top: 56px;
          z-index: 10;
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 16px 24px;
          background: linear-gradient(135deg, #141422, #0f0f18);
          border-bottom: 1px solid #1c1c2a;
        }

        .logo {
          font-size: 20px;
          font-weight: 700;
          letter-spacing: 0.08em;
        }

        .search {
          flex: 1;
          margin: 0 16px;
          position: relative;
        }

        .search input {
          width: 100%;
          padding: 10px 12px;
          border-radius: 8px;
          border: 1px solid #27273a;
          background: #10101a;
          color: var(--text);
        }

        .stats {
          margin: 16px 24px 0;
          color: var(--muted);
        }

        .grid {
          padding: 20px 24px;
          display: grid;
          grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
          gap: 20px;
        }

        .card {
          background: var(--surface);
          border-radius: 14px;
          overflow: hidden;
          border: 1px solid #1f1f2a;
          color: inherit;
          text-align: left;
          padding: 0;
          cursor: pointer;
          transition: transform 0.2s ease, box-shadow 0.2s ease;
        }

        .card:hover {
          transform: translateY(-4px);
          box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
        }

        .thumb {
          position: relative;
          height: 150px;
          background: #09090f;
          display: flex;
          align-items: center;
          justify-content: center;
        }

        .thumb img {
          width: 100%;
          height: 100%;
          object-fit: cover;
          display: block;
        }

        .thumb-fallback {
          font-size: 32px;
          color: var(--accent);
        }

        .thumb-overlay {
          position: absolute;
          inset: 0;
          display: flex;
          align-items: center;
          justify-content: center;
          background: rgba(0, 0, 0, 0.4);
          opacity: 0;
          font-size: 32px;
          transition: opacity 0.2s ease;
        }

        .card:hover .thumb-overlay {
          opacity: 1;
        }

        .badge {
          position: absolute;
          right: 10px;
          bottom: 10px;
          background: rgba(0, 0, 0, 0.75);
          padding: 4px 8px;
          border-radius: 6px;
          font-size: 12px;
        }

        .card-body {
          padding: 12px;
        }

        .card-body h3 {
          margin: 0 0 6px;
          font-size: 15px;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }

        .card-body p {
          margin: 0;
          color: var(--muted);
          font-size: 13px;
        }

        .skeleton {
          position: relative;
          overflow: hidden;
          background: #161620;
          border-radius: 14px;
          height: 230px;
        }

        .skeleton::after {
          content: '';
          position: absolute;
          top: 0;
          left: -100%;
          width: 100%;
          height: 100%;
          background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
          animation: shimmer 1.4s infinite;
        }

        @keyframes shimmer {
          100% {
            left: 100%;
          }
        }

        .load-more-sentinel {
          grid-column: 1 / -1;
          text-align: center;
          padding: 20px;
          color: var(--muted);
        }

        .load-more-spinner {
          display: inline-block;
          width: 20px;
          height: 20px;
          border: 2px solid var(--muted);
          border-top-color: var(--accent);
          border-radius: 50%;
          animation: spin 0.8s linear infinite;
        }

        @keyframes spin {
          to {
            transform: rotate(360deg);
          }
        }

        .modal-backdrop {
          position: fixed;
          inset: 0;
          background: rgba(5, 5, 10, 0.8);
          display: flex;
          align-items: center;
          justify-content: center;
          z-index: 30;
          padding: 20px;
        }

        .modal {
          background: #10101a;
          border-radius: 18px;
          max-width: 900px;
          width: 100%;
          padding: 24px;
          border: 1px solid #2a2a3d;
          position: relative;
        }

        .modal-video {
          background: #000;
          border-radius: 12px;
          overflow: hidden;
        }

        .modal-video video {
          width: 100%;
          max-height: 450px;
        }

        .modal-meta {
          margin-top: 16px;
        }

        .modal-meta h2 {
          margin: 0 0 8px;
        }

        .modal-meta p {
          margin: 0 0 16px;
          color: var(--muted);
        }

        .codec-warning {
          margin: 0 0 16px;
          padding: 10px 12px;
          border-radius: 10px;
          border: 1px solid #3a2b12;
          background: rgba(232, 184, 75, 0.08);
          color: #f0d58a;
          font-size: 13px;
          line-height: 1.4;
        }

        .modal-actions {
          display: flex;
          flex-wrap: wrap;
          gap: 10px;
        }

        .close {
          position: absolute;
          top: 12px;
          right: 12px;
          background: transparent;
          border: none;
          color: var(--muted);
          font-size: 24px;
          cursor: pointer;
        }

        .toast {
          position: fixed;
          right: 24px;
          bottom: 24px;
          padding: 12px 16px;
          border-radius: 10px;
          background: #12121a;
          border: 1px solid;
          z-index: 40;
          box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
        }

        .toast.success {
          border-color: var(--success);
        }

        .toast.error {
          border-color: var(--danger);
        }

        .error {
          margin: 12px 24px;
          color: var(--danger);
        }

        @media (max-width: 720px) {
          .config-bar,
          .header {
            flex-direction: column;
            align-items: stretch;
          }

          .header {
            top: 96px;
            gap: 12px;
          }

          .search {
            margin: 0;
          }
        }
      `}</style>

      <div className="config-bar">
        <input
          value={apiUrl}
          onChange={(event) => setApiUrl(event.target.value)}
          placeholder="Backend URL"
        />
        <button className="primary" type="button" onClick={() => fetchMovies(false)}>
          Connect
        </button>
      </div>

      <header className="header">
        <div className="logo">🎬 TGSTREAM</div>
        <div className="search">
          <input
            value={query}
            onChange={(event) => setQuery(event.target.value)}
            placeholder="Search movies..."
          />
        </div>
        <button className="primary" type="button" onClick={() => fetchMovies(true)}>
          Refresh
        </button>
      </header>

      <div className="stats">{statsLabel}</div>

      {error && <div className="error">{error}</div>}

      <section className="grid" ref={gridRef}>
        {loading
          ? Array.from({ length: 12 }).map((_, index) => (
              <div className="skeleton" key={`skeleton-${index}`} />
            ))
          : filtered.map((movie) => (
              <MovieCard key={movie.id} movie={movie} apiBase={apiBase} onOpen={setSelected} />
            ))}
        
        {filtered.length === 0 && !loading && movies.length === 0 && (
          <div className="load-more-sentinel">No movies yet. Click Connect to start.</div>
        )}
        
        {nextOffsetId && (
          <div className="load-more-sentinel">
            {loadingMore ? (
              <>
                <div className="load-more-spinner" /> Loading more...
              </>
            ) : (
              "Scroll to load more"
            )}
          </div>
        )}
      </section>

      {selected && (
        <Modal
          movie={selected}
          apiBase={apiBase}
          onClose={() => setSelected(null)}
        />
      )}

      <Toast toast={toast} />
    </div>
  );
}
