"use client";

import { Cloud, Lock, Monitor } from "lucide-react";
import type { ColumnDef } from "@tanstack/react-table";
import type { Agent, AgentRuntime } from "@multica/core/types";
import {
  type AgentActivity,
  type AgentPresenceDetail,
  summarizeActivityWindow,
  VISIBILITY_TOOLTIP,
} from "@multica/core/agents";
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@multica/ui/components/ui/tooltip";
import { ActorAvatar } from "../../common/actor-avatar";
import { availabilityConfig, workloadConfig } from "../presence";
import { AgentRowActions } from "./agent-row-actions";
import { Sparkline } from "./sparkline";
import { useT } from "../../i18n";

// Per-row data shape. We assemble agent + runtime + presence + activity +
// run count into one struct at the page level so the column cells just
// read off `row.original` without each pulling its own queries.
export interface AgentRow {
  agent: Agent;
  runtime: AgentRuntime | null;
  presence: AgentPresenceDetail | null | undefined;
  activity: AgentActivity | null | undefined;
  runCount: number;
  // Inline owner avatar — non-null when the page wants to attribute the
  // agent to a teammate (typically All scope on someone else's agent).
  ownerIdToShow: string | null;
  // True when the current user owns this agent (drives the "You" badge).
  isOwnedByMe: boolean;
  // True when the current user can archive / cancel-tasks on this agent.
  canManage: boolean;
}

// Sized columns render at exactly `size` in fixed table-layout mode —
// column.size doubles as the cell's effective max-width: truncatable
// cells with `truncate` inside hit ellipsis at the column edge.
//
// The Agent and Runtime columns have `meta.grow: true` so DataTable skips
// their inline widths until the user resizes them. Fixed table-layout splits
// the leftover space between them, which keeps Agent from monopolising wide
// viewports while still giving both columns a real floor.
//
// The grow columns also keep their `size` values even though those widths
// are skipped for initial rendering. TanStack folds them into
// `table.getTotalSize()`, which DataTable applies as the table's `min-width`.
// That's how the grow columns get real floors: when the viewport drops below
// the summed column sizes, the table refuses to shrink further and the
// container scrolls instead.
const COL_WIDTHS = {
  agent: 240,
  status: 120,
  workload: 140,
  runtime: 200,
  activity: 100,
  runs: 64,
  // 60 = 16 left padding + 28 kebab + 16 right padding. Keeps the
  // kebab's right edge 16px from the card so it lines up with the
  // toolbar's px-4 right inset.
  actions: 60,
} as const;

type ColumnHeaderT = ReturnType<typeof useT<"agents">>["t"];

function makeHeaderRenderer(t: ColumnHeaderT, key: "agent" | "status" | "workload" | "runtime" | "activity_7d" | "runs") {
  return key === "runs"
    ? () => <div className="text-right">{t(($) => $.columns.runs)}</div>
    : () => t(($) => $.columns[key]);
}

export function createAgentColumns({
  onDuplicate,
  t,
}: {
  onDuplicate: (agent: Agent) => void;
  t: ColumnHeaderT;
}): ColumnDef<AgentRow>[] {
  return [
    {
      id: "agent",
      header: makeHeaderRenderer(t, "agent"),
      size: COL_WIDTHS.agent,
      meta: { grow: true },
      cell: ({ row }) => <AgentNameCell row={row.original} />,
    },
    {
      id: "status",
      header: makeHeaderRenderer(t, "status"),
      size: COL_WIDTHS.status,
      cell: ({ row }) => {
        if (row.original.agent.archived_at) {
          return <span className="text-xs text-muted-foreground">—</span>;
        }
        return <AvailabilityCell presence={row.original.presence} />;
      },
    },
    {
      id: "workload",
      header: makeHeaderRenderer(t, "workload"),
      size: COL_WIDTHS.workload,
      cell: ({ row }) => {
        if (row.original.agent.archived_at) {
          return <span className="text-xs text-muted-foreground">—</span>;
        }
        return <WorkloadCell presence={row.original.presence} />;
      },
    },
    {
      id: "runtime",
      header: makeHeaderRenderer(t, "runtime"),
      size: COL_WIDTHS.runtime,
      meta: { grow: true },
      cell: ({ row }) => <RuntimeCell row={row.original} />,
    },
    {
      id: "activity",
      header: makeHeaderRenderer(t, "activity_7d"),
      size: COL_WIDTHS.activity,
      cell: ({ row }) => <ActivityCell row={row.original} />,
    },
    {
      id: "runs",
      header: makeHeaderRenderer(t, "runs"),
      size: COL_WIDTHS.runs,
      cell: ({ row }) => (
        <div className="text-right font-mono text-xs tabular-nums text-muted-foreground">
          {row.original.runCount == null
            ? "—"
            : row.original.runCount.toLocaleString()}
        </div>
      ),
    },
    {
      id: "actions",
      header: () => null,
      size: COL_WIDTHS.actions,
      enableResizing: false,
      cell: ({ row }) => (
        <div
          className="flex justify-end"
          // The kebab dropdown owns its own click target. Stop the row
          // click handler from firing as a side-effect.
          onClick={(e) => e.stopPropagation()}
        >
          <AgentRowActions
            agent={row.original.agent}
            presence={row.original.presence}
            canManage={row.original.canManage}
            onDuplicate={onDuplicate}
          />
        </div>
      ),
    },
  ];
}

// ---------------------------------------------------------------------------
// Cell renderers
// ---------------------------------------------------------------------------

function AgentNameCell({ row }: { row: AgentRow }) {
  const { t } = useT("agents");
  const { agent, ownerIdToShow, isOwnedByMe } = row;
  const isArchived = !!agent.archived_at;
  const isPrivate = agent.visibility === "private";

  return (
    <div className="flex min-w-0 items-center gap-3">
      <ActorAvatar
        actorType="agent"
        actorId={agent.id}
        size={28}
        className={`shrink-0 rounded-md ${isArchived ? "opacity-50 grayscale" : ""}`}
        showStatusDot
      />
      <div className="min-w-0 flex-1">
        <div className="flex min-w-0 items-center gap-2">
          <span
            className={`min-w-0 truncate font-medium ${
              isArchived ? "text-muted-foreground" : ""
            }`}
          >
            {agent.name}
          </span>
          {isPrivate && !isArchived && (
            <Tooltip>
              <TooltipTrigger
                render={
                  <Lock className="h-3 w-3 shrink-0 text-muted-foreground/60" />
                }
              />
              <TooltipContent>
                {VISIBILITY_TOOLTIP.private}
              </TooltipContent>
            </Tooltip>
          )}
          {isOwnedByMe && !ownerIdToShow && (
            <span className="shrink-0 rounded bg-muted px-1 text-[10px] font-medium text-muted-foreground">
              {t(($) => $.row.you)}
            </span>
          )}
          {ownerIdToShow && (
            <ActorAvatar
              actorType="member"
              actorId={ownerIdToShow}
              size={14}
            />
          )}
          {isArchived && (
            <span className="shrink-0 rounded-md bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
              {t(($) => $.row.archived)}
            </span>
          )}
        </div>
        <div
          className={`mt-0.5 truncate text-xs ${
            agent.description
              ? "text-muted-foreground"
              : "italic text-muted-foreground/50"
          }`}
        >
          {agent.description || t(($) => $.row.no_description)}
        </div>
      </div>
    </div>
  );
}

function AvailabilityCell({
  presence,
}: {
  presence: AgentPresenceDetail | null | undefined;
}) {
  const { t } = useT("agents");
  if (!presence) {
    return (
      <span className="inline-flex h-3 w-16 animate-pulse rounded bg-muted/60" />
    );
  }
  const av = availabilityConfig[presence.availability];
  return (
    <span className="inline-flex items-center gap-1.5">
      <span className={`h-1.5 w-1.5 shrink-0 rounded-full ${av.dotClass}`} />
      <span className={`text-xs ${av.textClass}`}>{t(($) => $.availability[presence.availability])}</span>
    </span>
  );
}

function WorkloadCell({
  presence,
}: {
  presence: AgentPresenceDetail | null | undefined;
}) {
  const { t } = useT("agents");
  if (!presence) {
    return (
      <span className="inline-flex h-3 w-20 animate-pulse rounded bg-muted/60" />
    );
  }
  // All three workload states render with the same shape (icon + label +
  // optional counts). Idle agents show "Idle" rather than a bare em-dash
  // — that hyphen used to mean both "no presence data" and "agent is
  // idle", which conflated two distinct things. Em-dash is now reserved
  // for archived rows / undefined presence (handled at the column level).
  const wl = workloadConfig[presence.workload];
  const isWorking = presence.workload === "working";
  const isQueued = presence.workload === "queued";
  // Queued's amber from workloadConfig is the severe tone for "stuck on
  // offline runtime". On an online runtime queued is just a brief race
  // between enqueue and daemon claim, where amber misreads as a warning.
  // Compose with availability so the colour matches the actual signal.
  const queuedTone =
    presence.availability === "online" ? "text-muted-foreground" : wl.textClass;
  const labelTone = isQueued ? queuedTone : wl.textClass;
  // Working: show running/capacity, optionally with +Nq when overflow.
  // Queued (= nothing running, things waiting — typically a stuck-on-
  // offline-runtime signal): show the queued count directly so the user
  // sees "Queued · 2" instead of misleading "Running 0/3 +2q".
  // Idle: no counts — the label alone carries the meaning.
  const counts = isWorking
    ? presence.queuedCount > 0
      ? `${presence.runningCount}/${presence.capacity} +${presence.queuedCount}q`
      : `${presence.runningCount}/${presence.capacity}`
    : isQueued
      ? `${presence.queuedCount}`
      : null;
  return (
    <span className="inline-flex items-center gap-1 text-xs">
      {/* Icon only renders for working/queued — those carry visual meaning
          (spinner = in motion, clock = waiting). Idle adding an icon read
          as a warning marker, which is the wrong signal. */}
      {presence.workload !== "idle" && (
        <wl.icon
          className={`h-3 w-3 shrink-0 ${labelTone} ${isWorking ? "animate-spin" : ""}`}
        />
      )}
      <span className={`shrink-0 ${labelTone}`}>{t(($) => $.workload[presence.workload])}</span>
      {counts && (
        <span className="truncate text-muted-foreground">{counts}</span>
      )}
    </span>
  );
}

function RuntimeCell({ row }: { row: AgentRow }) {
  const { t } = useT("agents");
  const { agent, runtime } = row;
  const isCloud = agent.runtime_mode === "cloud";
  const RuntimeIcon = isCloud ? Cloud : Monitor;
  const runtimeLabel = runtime?.name ?? (isCloud ? t(($) => $.row.fallback_runtime_cloud) : t(($) => $.row.fallback_runtime_local));

  return (
    <div className="flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">
      <RuntimeIcon className="h-3 w-3 shrink-0" />
      <Tooltip>
        <TooltipTrigger
          render={
            <span className="block min-w-0 truncate">{runtimeLabel}</span>
          }
        />
        <TooltipContent>{runtimeLabel}</TooltipContent>
      </Tooltip>
    </div>
  );
}

function ActivityCell({ row }: { row: AgentRow }) {
  const { agent, activity } = row;
  if (agent.archived_at) {
    return <span className="text-xs text-muted-foreground/50">—</span>;
  }
  if (!activity) {
    return (
      <span
        className="inline-block animate-pulse rounded bg-muted/60"
        style={{ width: 64, height: 20 }}
      />
    );
  }
  const summary = summarizeActivityWindow(activity, 7);
  return (
    <Tooltip>
      <TooltipTrigger
        render={
          <div className="inline-flex cursor-default items-center">
            <Sparkline buckets={summary.buckets} width={64} height={20} />
          </div>
        }
      />
      <TooltipContent>
        <ActivityTooltipBody activity={activity} />
      </TooltipContent>
    </Tooltip>
  );
}

function ActivityTooltipBody({ activity }: { activity: AgentActivity }) {
  const { t } = useT("agents");
  const summary = summarizeActivityWindow(activity, 7);
  const { totalRuns, totalFailed } = summary;
  const { daysSinceCreated } = activity;

  const isPartial = daysSinceCreated < 7;
  const headerText = isPartial
    ? daysSinceCreated === 0
      ? t(($) => $.activity_tooltip.created_today)
      : t(($) => $.activity_tooltip.created_days_ago, { count: daysSinceCreated })
    : t(($) => $.activity_tooltip.last_7_days);

  let bodyText: string;
  if (totalRuns === 0) {
    bodyText = t(($) => $.activity_tooltip.no_activity);
  } else {
    const runsText = t(($) => $.activity_tooltip.runs, { count: totalRuns });
    const failedFragment =
      totalFailed > 0
        ? t(($) => $.activity_tooltip.failed_suffix, {
            count: totalFailed,
            percent: Math.round((totalFailed / totalRuns) * 100),
          })
        : "";
    bodyText = `${runsText}${failedFragment}`;
  }

  return (
    <div className="flex flex-col gap-0.5">
      <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
        {headerText}
      </span>
      <span className="text-xs">{bodyText}</span>
    </div>
  );
}
