/* AMARINE — Portfolio index + route wrapper
   The router page id remains "portfolio" and this file decides between index and detail. */

function isVideoValue(value) {
  if (!value) return false;
  const source =
    typeof value === "string"
      ? value
      : value.src || value.url || value.href || "";
  // Cloudinary resource type is authoritative (handles extensionless URLs).
  if (/\/video\/upload\//i.test(source)) return true;
  if (/\/image\/upload\//i.test(source)) return false;
  return /\.(mp4|webm|mov|avi)$/i.test(source);
}

function buildPlaceholderMedia(label = "Portfolio") {
  const safeLabel = String(label || "Portfolio").replace(/&/g, "and");
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1500" viewBox="0 0 1200 1500"><rect width="1200" height="1500" fill="#f3efe5"/><rect x="80" y="80" width="1040" height="1340" rx="24" fill="#fffdf8" stroke="#d8c6a0" stroke-width="2"/><rect x="150" y="220" width="900" height="220" rx="12" fill="#e6dcc8"/><rect x="150" y="500" width="680" height="24" rx="12" fill="#caa86d"/><rect x="150" y="548" width="760" height="24" rx="12" fill="#d8c6a0"/><rect x="150" y="596" width="620" height="24" rx="12" fill="#d8c6a0"/><text x="600" y="1080" text-anchor="middle" font-family="Georgia, serif" font-size="56" fill="#374151">${safeLabel}</text></svg>`;
  return {
    type: "image",
    src: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
    poster: "",
  };
}

function normalizeMedia(value, fallbackLabel = "Portfolio") {
  if (!value || (typeof value === "string" && !value.trim())) {
    return buildPlaceholderMedia(fallbackLabel);
  }

  if (typeof value === "string") {
    return {
      type: isVideoValue(value) ? "video" : "image",
      src: value,
      poster: "",
    };
  }

  if (typeof value === "object") {
    const src = value.src || value.url || value.href || "";
    const poster = value.poster || value.thumbnail || value.image || "";
    return {
      type: value.type === "video" || isVideoValue(src) ? "video" : "image",
      src: src || poster || "",
      poster: poster || "",
    };
  }

  return buildPlaceholderMedia(fallbackLabel);
}

function getFolderNameFromMedia(value) {
  const media = normalizeMedia(value);
  const source = media.src || media.poster || "";
  const match = /assets\/media\/portafolio\/([^/]+)\//i.exec(source);
  if (match && match[1]) {
    return match[1].trim();
  }
  return "";
}

function toTitleWords(value) {
  if (!value) return "Project";
  return String(value)
    .trim()
    .replace(/[-_]+/g, " ")
    .replace(/\s+/g, " ")
    .split(" ")
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
    .join(" ");
}

function extractFolderTitle(rawValue) {
  const source = String(rawValue || "").trim();
  if (!source) return "";
  const firstToken = source.split("--")[0] || "";
  return toTitleWords(firstToken);
}

function resolveLocalizedText(localized, fallback, lang) {
  if (localized && typeof localized === "object") {
    if (localized[lang]) return localized[lang];
    if (localized.es) return localized.es;
    if (localized.en) return localized.en;
  }
  return fallback || "";
}

function getProjectDisplayTitle(item, lang) {
  const localizedTitle = resolveLocalizedText(item && item.titleI18n, item && item.title, lang);
  if (localizedTitle) return localizedTitle;
  if (item.folderName) return extractFolderTitle(item.folderName);
  const folderName = getFolderNameFromMedia(item.heroImage);
  if (folderName) return extractFolderTitle(folderName);
  if (item.title) return extractFolderTitle(item.title);
  return item.slug ? extractFolderTitle(item.slug) : "Project";
}

function isSafariBrowser() {
  if (typeof navigator === "undefined") return false;
  const ua = navigator.userAgent || "";
  return /Safari/i.test(ua) && !/Chrome|CriOS|Edg|OPR|Opera/i.test(ua);
}

function shouldReduceVideoLoading() {
  if (typeof window === "undefined") return true;
  const isSmallDevice = window.matchMedia && window.matchMedia("(max-width: 768px)").matches;
  const reducedMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  return isSmallDevice || reducedMotion || isSafariBrowser();
}

function getIndustryDisplayLabel(value, t) {
  const data = window.AMARINE_PORTFOLIO_DATA;
  const industryMeta =
    data && typeof data.getIndustryMeta === "function"
      ? data.getIndustryMeta(value)
      : null;
  const key =
    industryMeta && industryMeta.i18nKey
      ? industryMeta.i18nKey
      : `port.filter.industry.${value}`;
  const translated = t(key);
  if (translated && translated !== key) return translated;
  if (industryMeta && industryMeta.fallbackLabel)
    return industryMeta.fallbackLabel;
  return toTitleWords(value);
}

function prioritizeVideoInterleave(items) {
  if (!Array.isArray(items) || items.length < 2) {
    return Array.isArray(items) ? items.slice() : [];
  }

  const imagePool = [];
  const videoPool = [];

  items.forEach((item) => {
    const assets = [item.heroImage, ...(item.galleryImages || [])].filter(Boolean);
    const hasVideo = assets.some((value) => isVideoValue(value));
    if (hasVideo) {
      videoPool.push(item);
    } else {
      imagePool.push(item);
    }
  });

  const ordered = [];
  const total = items.length;

  for (let index = 0; index < total; index += 1) {
    const desiredType = index % 2 === 0 ? "image" : "video";
    const pool = desiredType === "image" ? imagePool : videoPool;

    if (pool.length) {
      ordered.push(pool.shift());
      continue;
    }

    const fallbackPool = desiredType === "image" ? videoPool : imagePool;
    if (fallbackPool.length) {
      ordered.push(fallbackPool.shift());
    }
  }

  return ordered.filter(Boolean);
}

function PortfolioCard({ item, idx, t }) {
  const lang = useLang();
  const mediaItems = React.useMemo(() => {
    const sources = [];
    const seenKeys = new Set();
    const addMedia = (value) => {
      const normalized = normalizeMedia(
        value,
        item.title || item.slug || "Portfolio",
      );
      const key = String(
        normalized.src || normalized.poster || "",
      ).toLowerCase();
      if (!key || seenKeys.has(key)) {
        return;
      }
      seenKeys.add(key);
      sources.push(normalized);
    };
    addMedia(item.heroImage);
    (item.galleryImages || []).forEach(addMedia);
    return sources;
  }, [item.heroImage, item.galleryImages]);

  const [activeIndex, setActiveIndex] = React.useState(0);
  const [shouldLoadVideo, setShouldLoadVideo] = React.useState(false);
  const cardRef = React.useRef(null);
  const title = getProjectDisplayTitle(item, lang);
  const hasVideo = mediaItems.some((media) => media.type === "video");
  const canCycleImages =
    mediaItems.length > 1 &&
    mediaItems.every((media) => media.type !== "video");

  const currentMedia = mediaItems[activeIndex] || mediaItems[0] || {};

  React.useEffect(() => {
    if (!hasVideo || !cardRef.current || !currentMedia.src) {
      setShouldLoadVideo(false);
      return undefined;
    }

    if (shouldReduceVideoLoading()) {
      const observer = new IntersectionObserver(
        ([entry]) => setShouldLoadVideo(entry.isIntersecting),
        { rootMargin: "220px 0px" },
      );
      observer.observe(cardRef.current);
      return () => observer.disconnect();
    }

    setShouldLoadVideo(true);
    return undefined;
  }, [hasVideo, currentMedia.src]);

  React.useEffect(() => {
    if (!mediaItems.length) return undefined;

    if (currentMedia.type === "video" && hasVideo) {
      return undefined;
    }

    const timeoutId = window.setTimeout(() => {
      setActiveIndex((prev) => (prev + 1) % mediaItems.length);
    }, 10000);

    return () => window.clearTimeout(timeoutId);
  }, [currentMedia.type, hasVideo, mediaItems.length, activeIndex]);

  const videoRef = React.useRef(null);

  const handleVideoEnded = () => {
    if (mediaItems.length === 1) {
      if (videoRef.current) {
        videoRef.current.currentTime = 0;
        videoRef.current.play();
      }
      return;
    }

    setActiveIndex((prev) => (prev + 1) % mediaItems.length);
  };

  return (
    <Reveal className="port-card" delay={(idx % 3) * 100}>
      <div ref={cardRef} className={`media-frame${canCycleImages ? " has-hover-media" : ""}`}>
        {currentMedia.type === "video" ? (
          <video
            ref={videoRef}
            key={`${item.slug}-${activeIndex}`}
            className="media-video"
            src={shouldLoadVideo ? currentMedia.src : undefined}
            poster={currentMedia.poster || undefined}
            autoPlay={shouldLoadVideo}
            muted
            playsInline
            preload={shouldReduceVideoLoading() ? "metadata" : "auto"}
            loop={false}
            controls={false}
            onEnded={handleVideoEnded}
          />
        ) : (
          <div className="media-stack">
            {mediaItems.map((media, index) => {
              const isActive = index === activeIndex;
              const isNext = index === (activeIndex + 1) % mediaItems.length;
              return (
                <div
                  key={`${media.src || media.poster || index}-${index}`}
                  className={`ph media-layer${isActive ? " media-layer-primary is-active" : ""}${isNext ? " media-layer-secondary" : ""}`}
                  style={{
                    backgroundImage: `url(${(media || {}).poster || (media || {}).src})`,
                  }}
                />
              );
            })}
          </div>
        )}
      </div>
      <div className="meta">
        <span>
          {item.city} · {resolveLocalizedText(item.industryLabelI18n, item.industryLabel || item.industry, lang)}
        </span>

        <span className="yr">{item.year}</span>
      </div>
      <h3>{title}</h3>
    </Reveal>
  );
}

function PortfolioIndexPage() {
  const t = useT();
  const [projects, setProjects] = React.useState(() => {
    const data = window.AMARINE_PORTFOLIO_DATA;
    if (data && typeof data.getProjects === "function") {
      return data.getProjects();
    }

    const fallbackProjects =
      window.AMARINE_DATA &&
      Array.isArray(window.AMARINE_DATA.PORTFOLIO_PROJECTS)
        ? window.AMARINE_DATA.PORTFOLIO_PROJECTS
        : [];
    return fallbackProjects;
  });

  React.useEffect(() => {
    document.body.classList.add("blend-header-logo");
    return () => document.body.classList.remove("blend-header-logo");
  }, []);

  React.useEffect(() => {
    const syncProjects = () => {
      const data = window.AMARINE_PORTFOLIO_DATA;
      if (data && typeof data.getProjects === "function") {
        setProjects(data.getProjects());
        return;
      }

      const fallbackProjects =
        window.AMARINE_DATA &&
        Array.isArray(window.AMARINE_DATA.PORTFOLIO_PROJECTS)
          ? window.AMARINE_DATA.PORTFOLIO_PROJECTS
          : [];
      setProjects(fallbackProjects);
    };

    syncProjects();
    const timer = window.setTimeout(syncProjects, 0);
    return () => window.clearTimeout(timer);
  }, []);

  const [industry, setIndustry] = React.useState("all");
  const [location, setLocation] = React.useState("all");

  React.useEffect(() => {
    const readLocation = () => {
      const params = new URLSearchParams(window.location.search);
      const queryLocation = params.get("location") || params.get("geo");
      if (!queryLocation) return;

      const validLocationValues = new Set(
        projects.flatMap((item) => [item.city, item.location, item.geo]).filter(Boolean),
      );

      if (validLocationValues.has(queryLocation) || queryLocation === "mexico" || queryLocation === "spain" || queryLocation === "usa") {
        setLocation(queryLocation);
      }
    };

    readLocation();
    if (window.AMARINE_ROUTER) return window.AMARINE_ROUTER.onChange(readLocation);
  }, [projects]);

  const filtered = projects.filter(
    (item) =>
      (industry === "all" ||
        (Array.isArray(item.industryTags)
          ? item.industryTags.includes(industry)
          : item.industry === industry)) &&
      (location === "all" ||
        item.city === location ||
        item.location === location ||
        item.geo === location),
  );

  const orderedFiltered = React.useMemo(
    () => prioritizeVideoInterleave(filtered),
    [filtered],
  );

  const indOpts = React.useMemo(() => {
    const values = Array.from(
      new Set(
        projects
          .flatMap((item) => {
            if (Array.isArray(item.industryTags) && item.industryTags.length)
              return item.industryTags;
            return item.industry ? [item.industry] : [];
          })
          .filter(Boolean),
      ),
    );
    return [
      { v: "all", l: t("port.filter.industry.all") },
      ...values.map((value) => ({
        v: value,
        l: getIndustryDisplayLabel(value, t),
      })),
    ];
  }, [projects, t]);
  const geoOpts = React.useMemo(() => {
    const values = Array.from(
      new Set(
        projects
          .map((item) => item.city || item.location || item.geo)
          .filter(Boolean),
      ),
    );
    return [
      { v: "all", l: t("port.filter.location.all") || t("port.filter.geo.all") },
      ...values.map((value) => ({
        v: value,
        l: value,
      })),
    ];
  }, [projects, t]);

  return (
    <>
      <Header active="portfolio" />
      <main>
        <section
          className="port-hero gut"
          style={{ paddingLeft: 0, paddingRight: 0 }}
        >
          <div className="gut">
            <Reveal>
              <span className="eyebrow">{t("port.eyebrow")}</span>
            </Reveal>
            <Reveal as="h1" delay={120}>
              {t("port.hero.title.part1")}{" "}
              <span className="serif-italic">{t("port.hero.title.part2")}</span>
            </Reveal>
            <Reveal delay={200}>
              <p>{t("port.hero.desc")}</p>
            </Reveal>

            <div className="port-filters">
              <FilterGroup
                label={t("port.filter.industry")}
                opts={indOpts}
                value={industry}
                onChange={setIndustry}
              />
              <FilterGroup
                label={t("port.filter.location")}
                opts={geoOpts}
                value={location}
                onChange={setLocation}
              />
              <div
                style={{
                  marginLeft: "auto",
                  alignSelf: "center",
                  fontFamily: "var(--sans)",
                  fontSize: 11,
                  letterSpacing: "0.16em",
                  textTransform: "uppercase",
                  color: "var(--mute)",
                }}
              >
                {filtered.length}{" "}
                {filtered.length === 1
                  ? t("port.count.one")
                  : t("port.count.many")}
              </div>
            </div>

            <div className="port-grid">
              {orderedFiltered.map((item, idx) => (
                <PortfolioCard key={item.slug} item={item} idx={idx} t={t} />
              ))}
            </div>

            {orderedFiltered.length === 0 && (
              <p
                style={{
                  fontFamily: "var(--serif)",
                  fontStyle: "italic",
                  fontSize: 24,
                  color: "var(--mute)",
                  textAlign: "center",
                  padding: "80px 0",
                }}
              >
                {t("port.empty")}
              </p>
            )}
          </div>
        </section>
      </main>
      <Footer />
    </>
  );
}

function FilterGroup({ label, opts, value, onChange }) {
  return (
    <div className="port-filter-group">
      <span className="label">{label}</span>
      <div className="opts">
        {opts.map((item) => (
          <button
            key={item.v}
            className={value === item.v ? "on" : ""}
            onClick={() => onChange(item.v)}
          >
            {item.l}
          </button>
        ))}
      </div>
    </div>
  );
}

function PortPage() {
  return <PortfolioIndexPage />;
}

window.AMARINE_PAGES = window.AMARINE_PAGES || {};
window.AMARINE_PAGES.PortfolioIndexPage = PortfolioIndexPage;
window.AMARINE_PAGES.PortPage = PortPage;
window.AMARINE_PAGES.portfolio = PortPage;
