"use client";

import { useParams } from "next/navigation";
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { Loader2 } from "lucide-react";
import { useAnatomy } from "@/components/project/use-anatomy";
import { usePreviewBridge } from "@/components/project/preview-bridge-context";
import { usePreviewToolbarStore } from "@/components/project/preview-toolbar/preview-toolbar-state";

const SAME_ORIGIN_SANDBOX =
  "allow-scripts allow-forms allow-same-origin allow-popups allow-modals allow-downloads" as const;

function sandboxIframe(src: string, origin: string): boolean {
  if (!src.startsWith("http://") && !src.startsWith("https://")) return true;
  try {
    return origin.length > 0 && new URL(src).origin === origin;
  } catch {
    return true;
  }
}

/** Elapsed time since an ISO string, formatted as "1m 23s". Capped at 59m 59s. */
function useElapsed(sinceIso: string | undefined) {
  const [elapsed, setElapsed] = useState("");
  useEffect(() => {
    if (!sinceIso) return;
    const update = () => {
      const ms = Date.now() - new Date(sinceIso).getTime();
      if (ms < 0) return;
      const s = Math.floor(ms / 1000);
      const m = Math.floor(s / 60);
      if (m > 59) {
        setElapsed("> 1h");
      } else {
        setElapsed(m > 0 ? `${m}m ${s % 60}s` : `${s}s`);
      }
    };
    update();
    const id = setInterval(update, 1000);
    return () => {
      clearInterval(id);
      setElapsed("");
    };
  }, [sinceIso]);
  return elapsed;
}

export default function PreviewTab() {
  const params = useParams();
  const projectId = params.projectId as string;

  // Poll every 5s so state transitions (starting→running, build complete, etc.)
  // surface without a manual refresh.
  const { anatomy, loading } = useAnatomy(projectId, { pollMs: 5000 });

  const previews = anatomy?.hosting.previews ?? [];

  const [now, setNow] = useState<number>(() => Date.now());
  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 10000);
    return () => clearInterval(id);
  }, []);

  // Identify all valid running/starting previews (ignoring stale starting ones)
  const validPreviews = previews
    .filter(
      (p) =>
        p.state === "running" ||
        (p.state === "starting" &&
          now - new Date(p.startedAt).getTime() < 15 * 60 * 1000),
    )
    .sort((a, b) => a.port - b.port); // sort ports ascending

  const [selectedPort, setSelectedPort] = useState<number>(3000);

  // Auto-select logic if selectedPort is not in validPreviews
  useEffect(() => {
    if (validPreviews.length === 0) return;
    const hasSelected = validPreviews.some((p) => p.port === selectedPort);
    if (!hasSelected) {
      // Prefer 3000 if available, else pick the first one
      const fallback =
        validPreviews.find((p) => p.port === 3000) ?? validPreviews[0];
      if (fallback) setSelectedPort(fallback.port);
    }
  }, [validPreviews, selectedPort]);

  // Derive the currently selected preview
  const activePreview = validPreviews.find((p) => p.port === selectedPort);

  // Split it into running and starting like before
  const primaryRunning =
    activePreview?.state === "running" ? activePreview : undefined;
  const primaryStarting =
    activePreview?.state === "starting" ? activePreview : undefined;

  // Derive in-flight / recently-failed build from prod apps.
  const liveApps = anatomy?.hosting.live ?? [];
  const inFlightApp = liveApps.find((a) => a.inFlightBuild);
  const failedApp = !inFlightApp
    ? liveApps.find((a) => a.lastBuild?.status === "failed")
    : undefined;

  // Fallback URL — the last deployed production app. Shown as a link while the
  // dev server is warming up so the user has something to interact with.
  const fallbackFqdn =
    liveApps.find((a) => a.fqdn && a.status === "running")?.fqdn ?? null;
  const fallbackUrl = fallbackFqdn
    ? fallbackFqdn.startsWith("http")
      ? fallbackFqdn
      : `https://${fallbackFqdn}`
    : null;

  // ── Auto-ensure: fire a background restart when the pane loads and finds
  //    no running dev server, but there's a previous config to restart from.
  const ensureCalledRef = useRef(false);
  const [ensureStatus, setEnsureStatus] = useState<
    "idle" | "calling" | "starting" | "no_history" | "error"
  >("idle");

  useEffect(() => {
    // Only trigger once per mount, and only when anatomy has loaded with no running server.
    if (ensureCalledRef.current) return;
    if (loading || !anatomy) return;
    if (primaryRunning || primaryStarting) return; // already up or already starting

    ensureCalledRef.current = true;

    fetch(`/api/projects/${projectId}/dev-server/ensure`, {
      method: "POST",
      credentials: "include",
    })
      .then((r) => r.json())
      .then((data: { status?: string }) => {
        if (data.status === "no_history" || data.status === "no_container") {
          setEnsureStatus("no_history");
        } else if (data.status === "starting" || data.status === "running") {
          setEnsureStatus("starting");
          // The 5s anatomy poll will pick up the new 'starting' row and
          // transition the pane automatically — no extra work needed here.
        } else {
          setEnsureStatus("idle");
        }
      })
      .catch(() => setEnsureStatus("error"));
  }, [loading, anatomy, primaryRunning, primaryStarting, projectId]);

  const [iframeSrc, setIframeSrc] = useState<string | null>(null);
  const iframeDomRef = useRef<HTMLIFrameElement | null>(null);
  const bridge = usePreviewBridge();
  const origin = typeof window !== "undefined" ? window.location.origin : "";

  const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
  const refreshKey = usePreviewToolbarStore((s) => s.refreshKey);

  // When the user clicks the manual refresh button in the toolbar, we don't
  // just want to reload the iframe — we also want to trigger the same ghost/zombie
  // check as the initial mount, in case the server died while they were looking at it.
  const prevRefreshKeyRef = useRef(refreshKey);
  useEffect(() => {
    if (refreshKey === prevRefreshKeyRef.current) return;
    prevRefreshKeyRef.current = refreshKey;

    // Reset the ensure called flag so the ensure effect (below) fires again.
    ensureCalledRef.current = false;
  }, [refreshKey]);

  useLayoutEffect(() => {
    setIframeSrc(primaryRunning?.url ?? null);
  }, [primaryRunning?.url]);

  useEffect(() => {
    if (!bridge || !iframeSrc || !iframeDomRef.current) return;
    bridge.registerPreviewIframe(iframeDomRef.current, iframeSrc);
  }, [bridge, iframeSrc]);

  const [isForceStarting, setIsForceStarting] = useState(false);

  // Determine which empty state to show.
  const emptyContent = (() => {
    if (loading && !anatomy) return <InitialLoader />;
    if (inFlightApp) return <BuildingState app={inFlightApp} />;
    if (failedApp) return <FailedState app={failedApp} />;
    // Dev server is in the process of booting (either picked up from anatomy
    // or we just fired the ensure endpoint and are waiting for the DB row).
    if (primaryStarting) {
      return (
        <WarmingUpState
          startedAt={primaryStarting.startedAt}
          fallbackUrl={fallbackUrl}
          port={selectedPort}
        />
      );
    }
    if (
      ensureStatus === "calling" ||
      ensureStatus === "starting" ||
      isForceStarting
    ) {
      return (
        <WarmingUpState
          startedAt={undefined}
          fallbackUrl={fallbackUrl}
          port={selectedPort}
        />
      );
    }
    // Never had a dev server — needs the AI to start one.
    return (
      <NotRunningState
        onStart={() => {
          setIsForceStarting(true);
          fetch(
            `/api/projects/${projectId}/dev-server/ensure?forceStart=true`,
            {
              method: "POST",
            },
          ).catch(() => setIsForceStarting(false));
        }}
      />
    );
  })();

  return (
    <div style={canvas}>
      {validPreviews.length > 1 && (
        <div style={portPickerWrap}>
          {validPreviews.map((p) => (
            <button
              key={p.id}
              onClick={() => setSelectedPort(p.port)}
              style={
                selectedPort === p.port ? portButtonActive : portButtonInactive
              }
            >
              <div
                style={p.state === "running" ? dotRunning : dotStarting}
                className={p.state === "starting" ? "animate-pulse" : ""}
              />
              {p.name && p.name !== `port-${p.port}`
                ? p.name
                : `Port ${p.port}`}
            </button>
          ))}
        </div>
      )}
      <div
        style={{
          flex: 1,
          minHeight: 0,
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <div
          style={{
            ...(deviceMode === "desktop" ? desktopFrame : mobileFrame),
            transition: "all 0.3s ease",
          }}
        >
          {deviceMode === "mobile" && <MobileChrome />}

          {iframeSrc ? (
            <iframe
              key={`${iframeSrc}-${refreshKey}`}
              src={iframeSrc}
              title="Preview"
              ref={(el) => {
                iframeDomRef.current = el;
                if (el) bridge?.registerPreviewIframe(el, iframeSrc);
              }}
              onLoad={() => bridge?.notifyPreviewIframeLoaded()}
              style={{
                ...iframeStyle,
                borderRadius: deviceMode === "mobile" ? 44 : 0,
              }}
              {...(sandboxIframe(iframeSrc, origin)
                ? { sandbox: SAME_ORIGIN_SANDBOX }
                : {})}
            />
          ) : (
            <div style={loaderWrap}>{emptyContent}</div>
          )}

          {deviceMode === "mobile" && <div style={homeIndicator} aria-hidden />}
        </div>
      </div>
    </div>
  );
}

// ── Empty-state sub-components ────────────────────────────────────────────────

function InitialLoader() {
  return (
    <Loader2
      className="animate-spin"
      style={{ width: 22, height: 22, color: "#9c9590" }}
    />
  );
}

function WarmingUpState({
  startedAt,
  fallbackUrl,
  port,
}: {
  startedAt: string | undefined;
  fallbackUrl: string | null;
  port?: number;
}) {
  const elapsed = useElapsed(startedAt);

  return (
    <div style={{ textAlign: "center", maxWidth: 280 }}>
      <div
        style={{ display: "flex", justifyContent: "center", marginBottom: 16 }}
      >
        <div style={buildRingOuter}>
          <div style={buildRingInner} className="animate-spin" />
        </div>
      </div>

      <p style={{ ...emptyTitle, color: "#6366f1" }}>
        Dev server {port ? `(:${port}) ` : ""}warming up
        {elapsed ? ` · ${elapsed}` : ""}
      </p>
      <p style={emptySubtext}>
        Your preview will appear here automatically once it&apos;s ready.
        Usually under 15 seconds.
      </p>

      {fallbackUrl && (
        <a
          href={fallbackUrl}
          target="_blank"
          rel="noopener noreferrer"
          style={{
            display: "inline-block",
            marginTop: 14,
            padding: "5px 12px",
            borderRadius: 20,
            background: "#f5f3ff",
            border: "1px solid #c4b5fd",
            fontSize: "0.72rem",
            color: "#7c3aed",
            textDecoration: "none",
          }}
        >
          View last deployed version →
        </a>
      )}
    </div>
  );
}

function BuildingState({
  app,
}: {
  app: {
    name: string;
    inFlightBuild?: { status: string; createdAt?: string; finishedAt?: string };
  };
}) {
  const elapsed = useElapsed(app.inFlightBuild?.createdAt);

  return (
    <div style={{ textAlign: "center", maxWidth: 280 }}>
      <div
        style={{ display: "flex", justifyContent: "center", marginBottom: 16 }}
      >
        <div style={buildRingOuter}>
          <div style={buildRingInner} className="animate-spin" />
        </div>
      </div>

      <p style={{ ...emptyTitle, color: "#3b82f6" }}>Building {app.name}</p>
      <p style={emptySubtext}>
        Your app is compiling and deploying. The preview will load automatically
        when it&apos;s ready.
      </p>

      <div style={statusPill}>
        <span
          style={{
            display: "inline-block",
            width: 6,
            height: 6,
            borderRadius: "50%",
            background: "#3b82f6",
            marginRight: 6,
            verticalAlign: "middle",
            animation: "pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
          }}
        />
        <span style={{ verticalAlign: "middle" }}>
          {app.inFlightBuild?.status ?? "building"}
          {elapsed ? ` · ${elapsed}` : ""}
        </span>
      </div>
    </div>
  );
}

function FailedState({
  app,
}: {
  app: { name: string; lastBuild?: { status: string; commit?: string } };
}) {
  return (
    <div style={{ textAlign: "center", maxWidth: 280 }}>
      <div
        style={{
          width: 40,
          height: 40,
          borderRadius: "50%",
          background: "#fef2f2",
          border: "1.5px solid #fecaca",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          margin: "0 auto 14px",
          fontSize: 18,
        }}
      >
        ✕
      </div>

      <p style={{ ...emptyTitle, color: "#ef4444" }}>Build failed</p>
      <p style={emptySubtext}>
        The last deploy of <strong>{app.name}</strong> didn&apos;t succeed.
        {app.lastBuild?.commit ? ` (${app.lastBuild.commit.slice(0, 7)})` : ""}
      </p>
      <p style={{ ...emptySubtext, marginTop: 8 }}>
        Ask the AI to check the build logs and fix the issue.
      </p>
    </div>
  );
}

function NotRunningState({ onStart }: { onStart: () => void }) {
  return (
    <div style={{ textAlign: "center", maxWidth: 260 }}>
      <div
        style={{
          width: 40,
          height: 40,
          borderRadius: "50%",
          background: "#f4f4f5",
          border: "1.5px solid #e4e4e7",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          margin: "0 auto 14px",
          fontSize: 18,
          color: "#a1a1aa",
        }}
      >
        ⏸
      </div>
      <p style={emptyTitle}>Preview not running</p>
      <p style={emptySubtext}>No dev server found on port 3000.</p>
      <button
        onClick={onStart}
        style={{
          marginTop: 16,
          background: "#18181b",
          color: "white",
          border: "none",
          padding: "8px 16px",
          borderRadius: 8,
          fontSize: "0.8rem",
          fontWeight: 500,
          cursor: "pointer",
          display: "inline-flex",
          alignItems: "center",
          gap: 6,
        }}
      >
        ⚡ Start Preview
      </button>
    </div>
  );
}

// ── Styles ────────────────────────────────────────────────────────────────────

const portPickerWrap: React.CSSProperties = {
  display: "flex",
  gap: 8,
  marginBottom: 12,
  padding: "0 4px",
  flexWrap: "wrap",
};

const portButtonBase: React.CSSProperties = {
  display: "flex",
  alignItems: "center",
  gap: 6,
  padding: "4px 12px",
  borderRadius: 20,
  fontSize: "0.75rem",
  fontWeight: 500,
  cursor: "pointer",
  transition: "all 0.15s ease",
  border: "1px solid transparent",
};

const portButtonActive: React.CSSProperties = {
  ...portButtonBase,
  background: "#fff",
  borderColor: "rgba(26, 26, 26, 0.08)",
  color: "#18181b",
  boxShadow: "0 1px 2px rgba(0,0,0,0.05)",
};

const portButtonInactive: React.CSSProperties = {
  ...portButtonBase,
  background: "transparent",
  color: "#71717a",
  border: "1px solid transparent",
};

const dotBase: React.CSSProperties = {
  width: 6,
  height: 6,
  borderRadius: "50%",
};

const dotRunning: React.CSSProperties = {
  ...dotBase,
  background: "#10b981", // green
  boxShadow: "0 0 6px rgba(16, 185, 129, 0.4)",
};

const dotStarting: React.CSSProperties = {
  ...dotBase,
  background: "#6366f1", // indigo
  boxShadow: "0 0 6px rgba(99, 102, 241, 0.4)",
};

const canvas: React.CSSProperties = {
  flex: 1,
  minHeight: 0,
  width: "100%",
  display: "flex",
  flexDirection: "column",
  alignSelf: "stretch",
  boxSizing: "border-box",
  padding: "14px 16px 18px",
  background: "linear-gradient(165deg, #faf8f5 0%, #f4f0ea 42%, #ebe7df 100%)",
};

const desktopFrame: React.CSSProperties = {
  flex: 1,
  width: "100%",
  height: "100%",
  display: "flex",
  flexDirection: "column",
  borderRadius: 14,
  overflow: "hidden",
  background: "#fff",
  border: "1px solid rgba(26, 26, 26, 0.07)",
  boxShadow:
    "0 1px 2px rgba(26, 26, 26, 0.04), 0 12px 40px rgba(26, 26, 26, 0.07), inset 0 1px 0 rgba(255, 255, 255, 0.85)",
};

const mobileFrame: React.CSSProperties = {
  position: "relative",
  width: 390,
  height: 844,
  flexShrink: 0,
  borderRadius: 56,
  overflow: "hidden",
  background: "#000",
  border: "14px solid #1a1a1a",
  boxShadow:
    "0 0 0 1px rgba(255,255,255,0.1) inset, 0 24px 60px rgba(26, 26, 26, 0.15)",
  display: "flex",
  flexDirection: "column",
};

const iframeStyle: React.CSSProperties = {
  flex: 1,
  width: "100%",
  height: "100%",
  border: "none",
  background: "#fff",
  pointerEvents: "auto",
};

const loaderWrap: React.CSSProperties = {
  flex: 1,
  display: "flex",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
  background: "#fff",
};

const emptyTitle: React.CSSProperties = {
  margin: "0 0 6px",
  fontSize: "0.9rem",
  fontWeight: 600,
  color: "#18181b",
  letterSpacing: "-0.01em",
};

const emptySubtext: React.CSSProperties = {
  margin: 0,
  fontSize: "0.78rem",
  color: "#71717a",
  lineHeight: 1.5,
};

const statusPill: React.CSSProperties = {
  display: "inline-block",
  marginTop: 14,
  padding: "4px 10px",
  borderRadius: 20,
  background: "#eff6ff",
  border: "1px solid #bfdbfe",
  fontSize: "0.72rem",
  color: "#1d4ed8",
};

const buildRingOuter: React.CSSProperties = {
  width: 40,
  height: 40,
  borderRadius: "50%",
  background: "#eff6ff",
  border: "2px solid #bfdbfe",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
};

const buildRingInner: React.CSSProperties = {
  width: 20,
  height: 20,
  borderRadius: "50%",
  borderTop: "2.5px solid #3b82f6",
  borderRight: "2.5px solid transparent",
  borderBottom: "2.5px solid transparent",
  borderLeft: "2.5px solid transparent",
};

function MobileChrome() {
  return (
    <div style={notchWrap}>
      <div style={notch} />
    </div>
  );
}

const notchWrap: React.CSSProperties = {
  position: "absolute",
  top: 0,
  left: 0,
  right: 0,
  height: 32,
  display: "flex",
  justifyContent: "center",
  zIndex: 10,
  pointerEvents: "none",
};

const notch: React.CSSProperties = {
  width: 120,
  height: 30,
  background: "#1a1a1a",
  borderBottomLeftRadius: 20,
  borderBottomRightRadius: 20,
};

const homeIndicator: React.CSSProperties = {
  position: "absolute",
  bottom: 8,
  left: "50%",
  transform: "translateX(-50%)",
  width: 140,
  height: 5,
  borderRadius: 10,
  background: "rgba(255, 255, 255, 0.4)",
  mixBlendMode: "difference",
  zIndex: 10,
  pointerEvents: "none",
};
