"use client";

/**
 * Project header status pill — surfaces what Coolify is actually doing.
 *
 * Priority (highest urgency wins):
 *   1. Build failed   — most recent finished deploy errored
 *   2. Deploying      — at least one in-flight deployment (queued / in_progress)
 *   3. Down           — apps exist but none running
 *   4. Live           — at least one app/service running healthy
 *   5. Empty          — no apps deployed yet (replaces the old false "Live"
 *                        fallback when data.status="active")
 *
 * while deploys or unhealthy states need attention (~8s); stays quiet when live or empty.
 * On hover the pill shows a tooltip with the breakdown of why we're
 * in the current state ("vibn-frontend is deploying", "twenty-live last
 * deploy failed 3m ago", etc.) — no more guessing.
 */

import { useMemo, useEffect, useState } from "react";
import { Loader2, ExternalLink } from "lucide-react";
import { useAnatomy, type Anatomy } from "./use-anatomy";

interface ProjectStagePillProps {
  projectId: string;
  /** Stage value pulled from fs_projects.data.status — only used while
   *  the first anatomy fetch is in flight, so the user sees something
   *  immediately instead of an empty header. */
  fallbackStage: "discovery" | "architecture" | "building" | "active";
}

type PillState =
  | { kind: "build_failed";  reason: string }
  | { kind: "deploying";     reason: string }
  | { kind: "down";          reason: string }
  | { kind: "live";          reason: string }
  | { kind: "empty";         reason: string };

export function ProjectStagePill({ projectId, fallbackStage }: ProjectStagePillProps) {
  const [anatomyPollMs, setAnatomyPollMs] = useState(0);
  const { anatomy, loading } = useAnatomy(projectId, { pollMs: anatomyPollMs });

  useEffect(() => {
    if (!anatomy) {
      setAnatomyPollMs(0);
      return;
    }
    const s = derivePillState(anatomy);
    setAnatomyPollMs(s.kind === "live" || s.kind === "empty" ? 0 : 8000);
  }, [anatomy]);

  const state = useMemo<PillState | null>(() => {
    if (!anatomy) return null;
    return derivePillState(anatomy);
  }, [anatomy]);

  if (loading && !anatomy) {
    const f = FALLBACK_PRESETS[fallbackStage];
    return <Pill label={f.label} color={f.color} bg={f.bg} title="Loading project status…" />;
  }
  if (!state) {
    const f = FALLBACK_PRESETS[fallbackStage];
    return <Pill label={f.label} color={f.color} bg={f.bg} title="Project status unavailable." />;
  }

  const visual = VISUALS[state.kind];

  // Deep-link target for the "Logs" affordance. Coolify v4 redirects
  // `/project/<uuid>` to that project's first environment, putting
  // the user one click from any application's deployment logs. We
  // don't store the environment UUID in anatomy (yet), so this is
  // the closest we can get without an extra Coolify call. When
  // anatomy exposes `lastBuildDeploymentUuid` we can deep-link
  // straight to `/applications/.../deployment/<dep-uuid>`.
  const coolifyBase = process.env.NEXT_PUBLIC_COOLIFY_URL ?? "";
  const coolifyProjectUuid = anatomy?.project?.coolifyProjectUuid;
  const coolifyDeepLink =
    coolifyBase && coolifyProjectUuid
      ? `${coolifyBase.replace(/\/$/, "")}/project/${coolifyProjectUuid}`
      : coolifyBase || null;

  // Show the "Logs" affordance whenever there's something interesting
  // happening Coolify-side: a build is in flight, the last build
  // failed, or apps are down. Hide on `live` and `empty` to avoid
  // visual noise when nothing's wrong.
  const showLogsLink =
    coolifyDeepLink &&
    (state.kind === "build_failed" ||
      state.kind === "deploying" ||
      state.kind === "down");

  const logsLinkColor =
    state.kind === "build_failed" || state.kind === "down"
      ? "#c5392b"
      : "#3d5afe";

  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
      <Pill
        label={visual.label}
        color={visual.color}
        bg={visual.bg}
        title={state.reason}
        spinning={state.kind === "deploying"}
      />
      {showLogsLink && (
        <a
          href={coolifyDeepLink}
          target="_blank"
          rel="noreferrer"
          title={`Open Coolify build logs in a new tab`}
          style={{
            display: "inline-flex", alignItems: "center", gap: 3,
            fontSize: "0.68rem", color: logsLinkColor,
            textDecoration: "none", opacity: 0.8,
          }}
        >
          Logs <ExternalLink size={9} />
        </a>
      )}
    </span>
  );
}

// Coolify reports container status as `<phase>` or `<phase>:<health>`,
// e.g. "running:healthy", "starting:unknown", "exited:unhealthy".
// Phase taxonomy:
//   running             → up
//   starting            → transient (booting / health-check pending)
//   restarting          → transient
//   created / paused    → transient (rare in our flow)
//   exited / dead       → down
// We classify each app, then aggregate to a pill state.
type AppPhase = "up" | "transient" | "down" | "unknown";
function classifyAppStatus(raw?: string): AppPhase {
  const s = (raw ?? "").toLowerCase().trim();
  if (!s || s === "unknown") return "unknown";
  if (/^(running|healthy)/.test(s)) return "up";
  if (/healthy/.test(s) && !/unhealthy/.test(s)) return "up";
  if (/^(starting|restarting|created|paused|deploying|building|in_progress|queued)/.test(s)) return "transient";
  if (/^(exited|dead|failed|stopped|unhealthy|error)/.test(s)) return "down";
  // Default to transient for anything unrecognised — Coolify occasionally
  // emits novel phases during upgrades; better to wait than mis-flag red.
  return "transient";
}

// Pure function. Exported-style intent only — keeps logic testable.
function derivePillState(a: Anatomy): PillState {
  const live = a.hosting?.live ?? [];

  if (live.length === 0) {
    return { kind: "empty", reason: "No apps deployed yet. Use the chat to spin one up." };
  }

  // 1. Active build in flight — highest priority signal.
  const deploying = live.filter((l) => l.inFlightBuild);
  if (deploying.length > 0) {
    const names = deploying.map((l) => l.name).join(", ");
    const stage = deploying[0].inFlightBuild?.status ?? "in progress";
    return { kind: "deploying", reason: `Deploying ${names}\nCoolify status: ${stage}` };
  }

  // 2. Container is currently booting (starting / restarting). Surface
  //    as "Deploying" since to the user this is the same wait state.
  const transient = live.filter((l) => classifyAppStatus(l.status) === "transient");
  if (transient.length > 0) {
    const lines = transient.map((l) => `${l.name}: ${l.status}`);
    return {
      kind: "deploying",
      reason: `Containers starting:\n${lines.join("\n")}`,
    };
  }

  // 3. Last finished build errored — call attention regardless of
  //    whether the previous container is still serving.
  const failed = live.filter(
    (l) => l.lastBuild && /fail|error|cancel/i.test(l.lastBuild.status),
  );
  if (failed.length > 0) {
    const lines = failed.map(
      (l) =>
        `${l.name}: ${l.lastBuild?.status}` +
        (l.lastBuild?.finishedAt ? ` · ${relTime(l.lastBuild.finishedAt)}` : ""),
    );
    return { kind: "build_failed", reason: `Last deploy failed:\n${lines.join("\n")}` };
  }

  const phases = live.map((l) => classifyAppStatus(l.status));
  const upCount = phases.filter((p) => p === "up").length;
  const downCount = phases.filter((p) => p === "down").length;

  if (upCount === live.length) {
    return {
      kind: "live",
      reason: `All ${live.length} ${live.length === 1 ? "service is" : "services are"} running.`,
    };
  }
  if (upCount > 0) {
    return { kind: "live", reason: `${upCount}/${live.length} services running.` };
  }
  if (downCount > 0) {
    const sample = live.slice(0, 3).map((l) => `${l.name}: ${l.status}`).join("\n");
    return { kind: "down", reason: `Apps are not running.\n${sample}` };
  }

  // All "unknown" — Coolify hasn't reported state yet (fresh project,
  // API hiccup). Treat as transient rather than red.
  return {
    kind: "deploying",
    reason: "Waiting on Coolify to report container state…",
  };
}

// ──────────────────────────────────────────────────

const VISUALS: Record<PillState["kind"], { label: string; color: string; bg: string }> = {
  build_failed: { label: "Build failed", color: "#c5392b", bg: "#c5392b14" },
  deploying:    { label: "Deploying",    color: "#3d5afe", bg: "#3d5afe10" },
  down:         { label: "Down",         color: "#c5392b", bg: "#c5392b14" },
  live:         { label: "Live",         color: "#2e7d32", bg: "#2e7d3210" },
  empty:        { label: "Empty",        color: "#7c7770", bg: "#a09a9014" },
};

const FALLBACK_PRESETS: Record<
  "discovery" | "architecture" | "building" | "active",
  { label: string; color: string; bg: string }
> = {
  discovery:    { label: "Defining", color: "#9a7b3a", bg: "#d4a04a14" },
  architecture: { label: "Planning", color: "#3d5afe", bg: "#3d5afe10" },
  building:     { label: "Building", color: "#3d5afe", bg: "#3d5afe10" },
  active:       { label: "Live",     color: "#2e7d32", bg: "#2e7d3210" },
};

function Pill({
  label, color, bg, title, spinning,
}: { label: string; color: string; bg: string; title?: string; spinning?: boolean }) {
  return (
    <span
      title={title}
      style={{
        display: "inline-flex", alignItems: "center", gap: 6,
        padding: "4px 10px", borderRadius: 4,
        fontSize: "0.7rem", fontWeight: 600, letterSpacing: "0.02em",
        color, background: bg, whiteSpace: "nowrap",
        cursor: title ? "help" : "default",
      }}
    >
      {spinning ? (
        <Loader2 size={9} className="animate-spin" style={{ color }} />
      ) : (
        <span style={{ width: 7, height: 7, borderRadius: "50%", background: color }} />
      )}
      {label}
    </span>
  );
}

function relTime(iso: string): string {
  const ms = Date.now() - new Date(iso).getTime();
  if (Number.isNaN(ms)) return "";
  const min = Math.floor(ms / 60_000);
  if (min < 1) return "just now";
  if (min < 60) return `${min}m ago`;
  const hr = Math.floor(min / 60);
  if (hr < 24) return `${hr}h ago`;
  return `${Math.floor(hr / 24)}d ago`;
}
