/* global React, ReactDOM */
const { useState, useEffect, useRef, useCallback } = React;

const cleanTitle = (t) => (t || "")
  .replace(/[\u202A-\u202E\u200E\u200F]/g, "")
  .replace(/\[[^\]]*\]/g, "")
  .replace(/\s+/g, " ")
  .trim();

async function loadVideos() {
  try {
    const r = await fetch("videos.json");
    if (!r.ok) throw new Error("videos.json " + r.status);
    const d = await r.json();
    return (d.videos || []).map((v) => ({ id: v.id, title: cleanTitle(v.title), thumb: v.thumb || `https://vumbnail.com/${v.id}_large.jpg` }));
  } catch (e) {
    console.error("videos.json load failed", e);
    return [];
  }
}

const isTouch = () => window.matchMedia("(pointer: coarse)").matches;
const scrollToId = (id) => {
  const el = document.getElementById(id);
  if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - (id === "top" ? 0 : 70), behavior: "smooth" });
};

function useGrain() {
  const [url, setUrl] = useState("");
  useEffect(() => {
    const c = document.createElement("canvas");
    c.width = c.height = 160;
    const x = c.getContext("2d");
    const img = x.createImageData(160, 160);
    for (let i = 0; i < img.data.length; i += 4) {
      const v = Math.random() * 255;
      img.data[i] = img.data[i + 1] = img.data[i + 2] = v;
      img.data[i + 3] = 255;
    }
    x.putImageData(img, 0, 0);
    setUrl(c.toDataURL());
  }, []);
  return url;
}

function Stage({ videos, active }) {
  const [videoId, setVideoId] = useState(null);
  const [ready, setReady] = useState(false);
  const cur = videos[active];
  useEffect(() => {
    setReady(false);
    setVideoId(null);
    if (!cur) return;
    const t = setTimeout(() => setVideoId(cur.id), 450);
    return () => clearTimeout(t);
  }, [cur && cur.id]);
  return (
    <div className="stage" aria-hidden="true">
      <div className="stage-media">
        {videos.map((v, i) => (
          <div key={v.id} className={`stage-img ${i === active ? "on" : ""}`} style={{ backgroundImage: `url("${v.thumb}")` }}></div>
        ))}
        {videoId && (
          <iframe key={videoId} className={`stage-video ${ready ? "ready" : ""}`} title=""
            src={`https://player.vimeo.com/video/${videoId}?background=1&autoplay=1&loop=1&muted=1&dnt=1`}
            allow="autoplay; fullscreen" onLoad={() => setTimeout(() => setReady(true), 700)}></iframe>
        )}
      </div>
      <div className="stage-scrim"></div>
    </div>
  );
}

function Header({ config, section }) {
  const [open, setOpen] = useState(false);
  const [solid, setSolid] = useState(false);
  useEffect(() => {
    const on = () => setSolid(window.scrollY > 40);
    on();
    window.addEventListener("scroll", on, { passive: true });
    return () => window.removeEventListener("scroll", on);
  }, []);
  useEffect(() => {
    document.body.style.overflow = open ? "hidden" : "";
  }, [open]);
  const L = (to, label) => (
    <a href={`#${to}`} className={section === to ? "on" : ""} onClick={(e) => { e.preventDefault(); setOpen(false); scrollToId(to); }}>{label}</a>
  );
  return (
    <>
      <header className={`head ${solid ? "solid" : ""}`}>
        <a href="#top" className="mark" onClick={(e) => { e.preventDefault(); setOpen(false); scrollToId("top"); }}>{config.name}</a>
        <div className="role">{config.role}</div>
        <div className="head-r">
          <nav className="nav">{L("work", "Work")}{L("about", "About")}{L("contact", "Contact")}</nav>
          <button className={`burger ${open ? "open" : ""}`} aria-label={open ? "Close menu" : "Open menu"} aria-expanded={open} onClick={() => setOpen((o) => !o)}>
            <span></span><span></span>
          </button>
        </div>
      </header>
      <div className={`drawer ${open ? "open" : ""}`} aria-hidden={!open}>
        <div className="drawer-links">{L("work", "Work")}{L("about", "About")}{L("contact", "Contact")}</div>
        <div className="drawer-foot">
          <a href={`mailto:${config.email}`}>{config.email}</a>
          <div>
            <a href={`https://instagram.com/${config.instagram}`} target="_blank" rel="noopener">Instagram</a>
            <a href={`https://vimeo.com/${config.vimeoUser}`} target="_blank" rel="noopener">Vimeo</a>
          </div>
        </div>
      </div>
    </>
  );
}

function Hero({ videos, active, setActive, onPlay, paused }) {
  const step = (d) => videos.length && setActive((active + d + videos.length) % videos.length);
  useEffect(() => {
    const k = (e) => {
      if (paused || window.scrollY > window.innerHeight * 0.5) return;
      if (e.key === "ArrowRight") step(1);
      if (e.key === "ArrowLeft") step(-1);
    };
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  });
  return (
    <section className="hero" id="top" data-screen-label="Hero">
      <Stage videos={videos} active={active} />
      {videos.length > 0 && (
        <>
          <button className="hit" aria-label={`Play ${videos[active].title}`} onClick={() => onPlay(active)}></button>
          <div className="bar">
            <div key={active} className="bar-title">{videos[active].title}</div>
            <div className="bar-r">
              <div className="count"><b>{String(active + 1).padStart(2, "0")}</b> / {String(videos.length).padStart(2, "0")}</div>
              <button className="arrow" aria-label="Previous film" onClick={() => step(-1)}>←</button>
              <button className="arrow" aria-label="Next film" onClick={() => step(1)}>→</button>
              <button className="play-btn" onClick={() => onPlay(active)}><i></i>Play</button>
            </div>
          </div>
        </>
      )}
    </section>
  );
}

function Tile({ v, onPlay }) {
  const [hover, setHover] = useState(false);
  const [ready, setReady] = useState(false);
  const timer = useRef(null);
  const touch = isTouch();
  const enter = () => { if (touch) return; timer.current = setTimeout(() => setHover(true), 300); };
  const leave = () => { clearTimeout(timer.current); setHover(false); setReady(false); };
  return (
    <article className="tile" onMouseEnter={enter} onMouseLeave={leave} onClick={onPlay}
      role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter") onPlay(); }} aria-label={`Play ${v.title}`}>
      <div className="tile-media">
        <img src={v.thumb} alt="" loading="lazy" />
        {hover && (
          <iframe title="" className={ready ? "ready" : ""}
            src={`https://player.vimeo.com/video/${v.id}?background=1&autoplay=1&loop=1&muted=1&dnt=1`}
            allow="autoplay" onLoad={() => setTimeout(() => setReady(true), 500)}></iframe>
        )}
        <span className="tile-play">Play</span>
      </div>
      <h3 className="tile-title">{v.title}</h3>
    </article>
  );
}

function Work({ videos, onPlay }) {
  return (
    <section className="sec" id="work" data-screen-label="Work">
      <div className="sec-head"><span>Work</span><span>{videos.length} films</span></div>
      <div className="grid">
        {videos.map((v, i) => <Tile key={v.id} v={v} onPlay={() => onPlay(i)} />)}
      </div>
    </section>
  );
}

function Player({ videos, index, setIndex, onClose }) {
  const v = index == null ? null : videos[index];
  useEffect(() => {
    document.body.style.overflow = v ? "hidden" : "";
    const k = (e) => {
      if (index == null) return;
      if (e.key === "Escape") onClose();
      if (e.key === "ArrowRight") setIndex((index + 1) % videos.length);
      if (e.key === "ArrowLeft") setIndex((index - 1 + videos.length) % videos.length);
    };
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  }, [index, videos.length]);
  return (
    <div className={`player ${v ? "open" : ""}`} role="dialog" aria-modal="true">
      <button className="player-close" onClick={onClose}>Close</button>
      {v && (
        <div className="player-inner">
          <div className="player-frame">
            <iframe key={v.id} title={v.title}
              src={`https://player.vimeo.com/video/${v.id}?autoplay=1&title=0&byline=0&portrait=0&badge=0&dnt=1`}
              allow="autoplay; fullscreen; picture-in-picture" allowFullScreen></iframe>
          </div>
          <div className="player-bar">
            <span className="ttl">{v.title}</span>
            <button onClick={() => setIndex((index + 1) % videos.length)}>Next film →</button>
          </div>
        </div>
      )}
    </div>
  );
}

function About() {
  return (
    <section className="sec" id="about" data-screen-label="About">
      <div className="sec-head"><span>About Me</span></div>
      <h2 className="big">Don't sell. Make people feel.</h2>
      <div className="about-grid">
        <div>
          <p>I'm Said Kassed, a director and cinematographer based in Casablanca, Morocco.</p>
          <p>I work in commercial films, music videos, and TVCs. My approach is simple: don't sell, make people feel. Every frame has a reason. Every cut has intention.</p>
          <p>Over the years I've worked across Morocco, Dubai, Abu Dhabi, Istanbul, New York, and beyond. I bring the same energy to every project. Cinematic, story-driven, and built to last.</p>
          <p>I believe great work comes from trust. Between director and client. Between vision and execution. I don't just show up to shoot. I come with ideas, with references, with a point of view.</p>
        </div>
        <div>
          <h3>Let's Work Together</h3>
          <p>I'm open to collaborations on commercial projects, music videos, branded content, and TVCs, locally and internationally.</p>
          <p>If you have a project coming up and you're looking for a director or DP who's serious about the craft, let's talk.</p>
          <p>I work as a freelancer, which means I move fast, I adapt, and I bring full focus to every project I take on. No agency layers. Direct communication. Clean results.</p>
          <p>Reach out and let's build something worth watching.</p>
        </div>
      </div>
    </section>
  );
}

function Contact({ config }) {
  const [copied, setCopied] = useState(false);
  const copy = async () => {
    try { await navigator.clipboard.writeText(config.email); setCopied(true); setTimeout(() => setCopied(false), 1600); }
    catch (e) { location.href = `mailto:${config.email}`; }
  };
  return (
    <section className="sec" id="contact" data-screen-label="Contact">
      <div className="sec-head"><span>Contact</span></div>
      <h2 className="big">Let's build something worth watching.</h2>
      <div>
        <button className="mail" onClick={copy}>{config.email}<small>{copied ? "Copied" : "Copy"}</small></button>
      </div>
      <div className="contact-row">
        <a href={`mailto:${config.email}`}>Write an email</a>
        <a href={`https://instagram.com/${config.instagram}`} target="_blank" rel="noopener">Instagram</a>
        <a href={`https://vimeo.com/${config.vimeoUser}`} target="_blank" rel="noopener">Vimeo</a>
      </div>
      <div className="foot">© {new Date().getFullYear()} Said Kassed</div>
    </section>
  );
}

const FONT_OPTIONS = ["Helvetica Neue", "Arial", "Inter Tight", "Manrope", "Space Grotesk", "Archivo", "Anton", "EB Garamond", "IBM Plex Mono"];
const SYSTEM_FONTS = ["Helvetica Neue", "Arial"];

function Tweaks({ config, setConfig }) {
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const onMsg = (e) => {
      if (!e.data || !e.data.type) return;
      if (e.data.type === "__activate_edit_mode") setOpen(true);
      if (e.data.type === "__deactivate_edit_mode") setOpen(false);
    };
    window.addEventListener("message", onMsg);
    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
    return () => window.removeEventListener("message", onMsg);
  }, []);
  const set = (k, v) => {
    setConfig((c) => ({ ...c, [k]: v }));
    window.parent.postMessage({ type: "__edit_mode_set_keys", edits: { [k]: v } }, "*");
  };
  return (
    <div className={`tweaks ${open ? "open" : ""}`}>
      <h5>Tweaks</h5>
      <label>Font
        <select value={config.fontFamily} onChange={(e) => set("fontFamily", e.target.value)}>
          {FONT_OPTIONS.map((f) => <option key={f} value={f}>{f}</option>)}
        </select>
      </label>
      <label>Text size · {Math.round((config.fontScale || 1) * 100)}%
        <input type="range" min="0.7" max="1.5" step="0.05" value={config.fontScale || 1} onChange={(e) => set("fontScale", parseFloat(e.target.value))} />
      </label>
      <label className="row">Auto-advance films<input type="checkbox" checked={!!config.autoAdvance} onChange={(e) => set("autoAdvance", e.target.checked)} /></label>
      <label className="row">Film grain<input type="checkbox" checked={!!config.grain} onChange={(e) => set("grain", e.target.checked)} /></label>
      <button className="reset" onClick={() => { set("fontFamily", "Helvetica Neue"); set("fontScale", 1); }}>Reset type</button>
    </div>
  );
}

function Loader({ name, onDone }) {
  const [out, setOut] = useState(false);
  useEffect(() => {
    const a = setTimeout(() => setOut(true), 1450);
    const b = setTimeout(onDone, 2450);
    return () => { clearTimeout(a); clearTimeout(b); };
  }, []);
  return (
    <div className={`loader ${out ? "out" : ""}`}>
      <div className="loader-name">{name}</div>
      <div className="loader-line"></div>
    </div>
  );
}

function App() {
  const [config, setConfig] = useState(window.SITE_CONFIG);
  const [videos, setVideos] = useState([]);
  const [active, setActiveRaw] = useState(0);
  const [playing, setPlaying] = useState(null);
  const [section, setSection] = useState("top");
  const [intro, setIntro] = useState(() => !sessionStorage.getItem("sk_intro"));
  const touched = useRef(false);
  const grain = useGrain();

  useEffect(() => { loadVideos().then(setVideos); }, []);

  useEffect(() => {
    const ids = ["top", "work", "about", "contact"];
    const on = () => {
      let cur = "top";
      ids.forEach((id) => { const el = document.getElementById(id); if (el && el.getBoundingClientRect().top <= window.innerHeight * 0.45) cur = id; });
      setSection(cur);
    };
    on();
    window.addEventListener("scroll", on, { passive: true });
    return () => window.removeEventListener("scroll", on);
  }, [videos.length]);

  const setActive = useCallback((i) => { touched.current = true; setActiveRaw(i); }, []);

  useEffect(() => {
    if (!config.autoAdvance || playing != null || !videos.length) return;
    const t = setInterval(() => { if (!touched.current) setActiveRaw((a) => (a + 1) % videos.length); }, 7000);
    return () => clearInterval(t);
  }, [config.autoAdvance, playing, videos.length]);

  useEffect(() => {
    const s = document.documentElement.style;
    s.setProperty("--font", `"${config.fontFamily}", "Helvetica Neue", Helvetica, Arial, sans-serif`);
    s.setProperty("--fs", String(config.fontScale || 1));
    if (!SYSTEM_FONTS.includes(config.fontFamily)) {
      const id = "gf-" + config.fontFamily.replace(/\s/g, "");
      if (!document.getElementById(id)) {
        const l = document.createElement("link");
        l.id = id; l.rel = "stylesheet";
        l.href = `https://fonts.googleapis.com/css2?family=${config.fontFamily.replace(/\s/g, "+")}:wght@400;500;600&display=swap`;
        document.head.appendChild(l);
      }
    }
  }, [config.fontFamily, config.fontScale]);

  const endIntro = () => { sessionStorage.setItem("sk_intro", "1"); setIntro(false); };

  return (
    <>
      {config.grain && grain && <div className="grain" style={{ backgroundImage: `url(${grain})` }} aria-hidden="true"></div>}
      <Header config={config} section={section} />
      <Hero videos={videos} active={active} setActive={setActive} onPlay={setPlaying} paused={playing != null} />
      <Work videos={videos} onPlay={setPlaying} />
      <About />
      <Contact config={config} />
      <Player videos={videos} index={playing} setIndex={setPlaying} onClose={() => setPlaying(null)} />
      {intro && <Loader name={config.name} onDone={endIntro} />}
      <Tweaks config={config} setConfig={setConfig} />
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
