import { useMemo, useState } from "react";
import { ApiError } from "../../api/client";
import type { Session } from "../../api/types";
import { useAuth } from "../../auth/AuthContext";
import { PageHeader } from "../../components/layout/PageHeader";
import {
  JoinIcon,
  RefreshIcon,
  SearchIcon,
  StopIcon,
} from "../../components/layout/icons";
import { Badge } from "../../components/ui/Badge";
import { Button } from "../../components/ui/Button";
import { Input } from "../../components/ui/Input";
import { Panel } from "../../components/ui/Panel";
import { EmptyState, ErrorState } from "../../components/ui/States";
import { StatusDot } from "../../components/ui/StatusDot";
import { consentLabel, consentTone } from "../../components/ui/status";
import { Table, type Column } from "../../components/ui/Table";
import { TableSkeleton } from "../../components/ui/TableSkeleton";
import { absoluteTime, relativeTime } from "../../lib/time";
import { EndSessionDialog } from "./EndSessionDialog";
import { JoinSessionModal } from "./JoinSessionModal";
import { useSessions } from "./hooks";
import "./sessions.css";

/** Live elapsed time since an ISO start, formatted like the duration helper. */
function elapsedSince(iso: string): string {
  const start = new Date(iso).getTime();
  if (Number.isNaN(start)) return "—";
  const secs = Math.max(0, Math.floor((Date.now() - start) / 1000));
  const h = Math.floor(secs / 3600);
  const m = Math.floor((secs % 3600) / 60);
  const s = secs % 60;
  if (h > 0) return `${h}h ${String(m).padStart(2, "0")}m`;
  if (m > 0) return `${m}m ${String(s).padStart(2, "0")}s`;
  return `${s}s`;
}

export function SessionsPage() {
  const { isAdmin, hasPermission } = useAuth();
  const sessionsQuery = useSessions();
  const [filter, setFilter] = useState("");

  const [joinFor, setJoinFor] = useState<Session | null>(null);
  const [endFor, setEndFor] = useState<Session | null>(null);

  // The same authz split the server enforces at mint time: admin OR the
  // `control` permission yields a control token; otherwise the floor is `view`.
  // We mirror it so we never present a control affordance the server downgrades,
  // and so a user with neither permission gets no join action at all (the mint
  // would 403).
  const canControl = isAdmin || hasPermission("control");
  const canView = canControl || hasPermission("view");

  const { data } = sessionsQuery;
  const sessions = useMemo(() => data ?? [], [data]);

  const filtered = useMemo(() => {
    const q = filter.trim().toLowerCase();
    if (!q) return sessions;
    return sessions.filter(
      (s) =>
        s.agent_name.toLowerCase().includes(q) ||
        s.agent_id.toLowerCase().includes(q),
    );
  }, [sessions, filter]);

  const onlineCount = useMemo(
    () => sessions.filter((s) => s.is_online).length,
    [sessions],
  );

  // A viewer may only join sessions the relay would admit: not_required or
  // granted. pending/denied/offline cannot be joined, so the join action is
  // disabled with a reason rather than offered and rejected.
  function joinBlockedReason(s: Session): string | null {
    if (!s.is_online) return "Agent is offline";
    if (s.consent_state === "pending") return "Waiting on end-user consent";
    if (s.consent_state === "denied") return "Consent was denied";
    return null;
  }

  const columns: Column<Session>[] = [
    {
      key: "status",
      header: "",
      cellClass: "dt__status",
      render: (s) => (
        <StatusDot
          tone={s.is_online ? "ok" : "bad"}
          label={s.is_online ? "online" : "offline"}
        />
      ),
    },
    {
      key: "machine",
      header: "Machine",
      render: (s) => (
        <div className="sdt__machine">
          <span className="dt__strong">{s.agent_name}</span>
          <span className="dt__mono sdt__agentid" title={s.agent_id}>
            {s.agent_id}
          </span>
        </div>
      ),
    },
    {
      key: "mode",
      header: "Mode",
      render: (s) =>
        s.is_persistent ? (
          <Badge tone="accent">Managed</Badge>
        ) : (
          <Badge tone="neutral">Attended</Badge>
        ),
    },
    {
      key: "consent",
      header: "Consent",
      render: (s) => (
        <Badge tone={consentTone(s.consent_state)} dot>
          {consentLabel(s.consent_state)}
        </Badge>
      ),
    },
    {
      key: "viewers",
      header: "Viewers",
      render: (s) =>
        s.viewer_count > 0 ? (
          <span className="sdt__viewers" title={s.viewers.map((v) => v.name).join(", ")}>
            <span className="dt__strong">{s.viewer_count}</span> watching
          </span>
        ) : (
          <span className="dt__muted">None</span>
        ),
    },
    {
      key: "started",
      header: "Started",
      render: (s) => (
        <span className="dt__mono" title={absoluteTime(s.started_at)}>
          {relativeTime(s.started_at)}
        </span>
      ),
    },
    {
      key: "duration",
      header: "Duration",
      render: (s) => (
        <span className="dt__mono">
          {s.is_online ? elapsedSince(s.started_at) : "—"}
        </span>
      ),
    },
    {
      key: "actions",
      header: "",
      cellClass: "dt__actions",
      render: (s) => {
        const blocked = joinBlockedReason(s);
        return (
          <span
            className="dt__rowactions"
            onClick={(e) => e.stopPropagation()}
          >
            {canView && (
              <Button
                variant="primary"
                size="sm"
                onClick={() => setJoinFor(s)}
                disabled={blocked != null}
                title={blocked ?? undefined}
                aria-label={
                  blocked
                    ? `${canControl ? "Control" : "View"} ${s.agent_name} (unavailable: ${blocked})`
                    : `${canControl ? "Control" : "View"} ${s.agent_name}`
                }
              >
                <JoinIcon width={14} height={14} />
                {canControl ? "Control" : "View"}
              </Button>
            )}
            <Button
              variant="danger"
              size="sm"
              onClick={() => setEndFor(s)}
              disabled={!s.is_online}
              title={!s.is_online ? "Agent is offline" : undefined}
              aria-label={`End session on ${s.agent_name}`}
            >
              <StopIcon width={14} height={14} />
              End
            </Button>
          </span>
        );
      },
    },
  ];

  return (
    <div className="page">
      <PageHeader
        title="Sessions"
        subtitle="Live remote sessions across the relay. Consent transitions update on their own."
        actions={
          <Button
            variant="ghost"
            onClick={() => void sessionsQuery.refetch()}
            loading={sessionsQuery.isFetching}
          >
            <RefreshIcon width={15} height={15} />
            Refresh
          </Button>
        }
      />

      <Panel flush>
        <div style={{ padding: "14px 16px 0" }}>
          <div className="toolbar">
            <div className="searchbox">
              <span className="searchbox__icon">
                <SearchIcon width={15} height={15} />
              </span>
              <Input
                placeholder="Filter by machine or agent ID"
                value={filter}
                onChange={(e) => setFilter(e.target.value)}
                aria-label="Filter sessions"
              />
            </div>
            <div className="toolbar__count">
              <span className="mono">{onlineCount}</span> live ·{" "}
              <span className="mono">{sessions.length}</span> total
            </div>
          </div>
        </div>

        {sessionsQuery.isLoading ? (
          <>
            <span className="visually-hidden" role="status">
              Loading sessions
            </span>
            <TableSkeleton
              headers={[
                "",
                "Machine",
                "Mode",
                "Consent",
                "Viewers",
                "Started",
                "Duration",
                "",
              ]}
            />
          </>
        ) : sessionsQuery.isError ? (
          <ErrorState
            title="Could not load sessions"
            message={
              sessionsQuery.error instanceof ApiError
                ? sessionsQuery.error.message
                : "The GuruConnect relay did not respond. Check the relay status, then retry."
            }
            action={
              <Button variant="primary" onClick={() => void sessionsQuery.refetch()}>
                Retry
              </Button>
            }
          />
        ) : filtered.length === 0 ? (
          filter ? (
            <EmptyState
              title="No matching sessions"
              message={`Nothing matches "${filter}". Clear the filter to see every live session.`}
              action={
                <Button variant="ghost" onClick={() => setFilter("")}>
                  Clear filter
                </Button>
              }
            />
          ) : (
            <EmptyState
              title="No active sessions"
              message="Sessions appear here when a managed agent connects or a support code is redeemed. Start one from the Machines view or hand a support code to an end user."
            />
          )
        ) : (
          <Table
            columns={columns}
            rows={filtered}
            rowKey={(s) => s.id}
          />
        )}
      </Panel>

      <JoinSessionModal
        session={joinFor}
        canControl={canControl}
        onClose={() => setJoinFor(null)}
      />
      <EndSessionDialog session={endFor} onClose={() => setEndFor(null)} />
    </div>
  );
}
