import { db } from "@/db/client";
import { storageAvailability, storageFiles, storageTiers } from "@/db/schema";
import { AGENT_TIMEOUT_SECONDS, formatSince, getAgentStatuses } from "@/lib/agents";
import { desc, sql } from "drizzle-orm";

export const dynamic = "force-dynamic";

function formatBytes(value: number | null) {
  if (!value || value <= 0) return "0 B";
  const units = ["B", "KB", "MB", "GB", "TB"];
  let size = value;
  let unit = 0;
  while (size >= 1024 && unit < units.length - 1) {
    size /= 1024;
    unit += 1;
  }
  return size.toFixed(unit === 0 ? 0 : 1) + " " + units[unit];
}

type AgentPath = { path: string; exists: boolean; mounted: boolean; readable: boolean; freeBytes: number };

function agentPaths(details: unknown): AgentPath[] {
  const paths = (details as { paths?: unknown } | null)?.paths;
  return Array.isArray(paths) ? (paths as AgentPath[]) : [];
}

function formatDate(value: Date | null) {
  return value ? value.toISOString().slice(0, 16).replace("T", " ") : "Open";
}

export default async function AdminStoragePage() {
  const agents = await getAgentStatuses();
  const agentByName = new Map(agents.map((agent) => [agent.agent, agent]));

  const [tiers, availabilityRows, inventoryRows] = await Promise.all([
    db.select().from(storageTiers).orderBy(storageTiers.tier),
    db.select().from(storageAvailability).orderBy(desc(storageAvailability.detectedAt)).limit(100),
    db
      .select({
        tierId: storageFiles.tierId,
        fileCount: sql<string>`count(*)::text`,
        movieFileCount: sql<string>`count(*) filter (where ${storageFiles.relativePath} like 'Movies/%' or ${storageFiles.relativePath} like 'Movies-EN/%')::text`,
        tvFileCount: sql<string>`count(*) filter (where ${storageFiles.relativePath} like 'Television/%')::text`,
        totalSize: sql<string>`pg_size_pretty(coalesce(sum(${storageFiles.sizeBytes}), 0))`,
        movieSize: sql<string>`pg_size_pretty(coalesce(sum(${storageFiles.sizeBytes}) filter (where ${storageFiles.relativePath} like 'Movies/%' or ${storageFiles.relativePath} like 'Movies-EN/%'), 0))`,
        tvSize: sql<string>`pg_size_pretty(coalesce(sum(${storageFiles.sizeBytes}) filter (where ${storageFiles.relativePath} like 'Television/%'), 0))`,
      })
      .from(storageFiles)
      .groupBy(storageFiles.tierId),
  ]);

  const availabilityByTier = new Map<string, typeof availabilityRows>();
  for (const row of availabilityRows) {
    availabilityByTier.set(row.tierId, [...(availabilityByTier.get(row.tierId) ?? []), row]);
  }
  const inventoryByTier = new Map(inventoryRows.map((row) => [row.tierId, row]));

  return (
    <div className="space-y-6">
      <div>
        <p className="text-xs font-semibold uppercase tracking-[0.32em] text-admin-accent">Storage</p>
        <h1 className="mt-2 font-serif text-3xl font-semibold">Storage Tiers</h1>
      </div>
      <section className="admin-panel p-5">
        <div className="flex flex-wrap items-baseline justify-between gap-2">
          <h2 className="font-serif text-xl font-semibold">Agents</h2>
          <p className="text-xs text-admin-muted">Considered offline after {AGENT_TIMEOUT_SECONDS}s of silence</p>
        </div>
        {agents.length ? (
          <div className="mt-4 grid gap-3 lg:grid-cols-2">
            {agents.map((agent) => {
              const paths = agentPaths(agent.details);
              return (
                <div key={agent.agent} className="rounded-md border border-admin-line bg-admin-subpanel p-4">
                  <div className="flex items-center justify-between gap-3">
                    <div>
                      <p className="font-semibold">{agent.agent}</p>
                      <p className="text-xs text-admin-muted">{agent.hostname ?? "unknown host"}</p>
                    </div>
                    <span className={agent.online ? "text-sm font-semibold text-admin-good" : "text-sm font-semibold text-admin-warn"}>
                      {agent.online ? "Online" : "Offline"}
                    </span>
                  </div>
                  <p className="mt-2 text-xs text-admin-muted">Last beat {formatSince(agent.secondsSinceSeen)}</p>
                  {paths.length ? (
                    <ul className="mt-3 space-y-1 text-xs">
                      {paths.map((path) => (
                        <li key={path.path} className="flex items-center justify-between gap-2">
                          <span className="break-all text-admin-muted">{path.path}</span>
                          <span className={path.readable ? "shrink-0 text-admin-good" : "shrink-0 text-admin-warn"}>
                            {path.readable ? formatBytes(path.freeBytes) + " free" : "unreachable"}
                          </span>
                        </li>
                      ))}
                    </ul>
                  ) : null}
                </div>
              );
            })}
          </div>
        ) : (
          <p className="mt-3 text-sm text-admin-muted">
            No agents have reported yet. See <span className="font-mono">agent/README.md</span> to install the herald on silenus or edda.
          </p>
        )}
      </section>

      <div className="grid gap-5 xl:grid-cols-3">
        {tiers.length ? tiers.map((tier) => {
          const events = availabilityByTier.get(tier.id) ?? [];
          const latest = events[0];
          // When a tier names an agent, that agent's heartbeat is authoritative:
          // storage on a powered-off host is not reachable no matter what the
          // last scan observed.
          const agent = tier.agentName ? agentByName.get(tier.agentName) : undefined;
          const online = tier.agentName
            ? Boolean(agent?.online)
            : tier.alwaysOnline || latest?.online;
          const inventory = inventoryByTier.get(tier.id);
          return (
            <section key={tier.id} className="admin-panel p-5">
              <div className="flex items-start justify-between gap-4">
                <div>
                  <p className="text-xs font-semibold uppercase tracking-[0.24em] text-admin-muted">{tier.tier}</p>
                  <h2 className="mt-2 font-serif text-2xl font-semibold">{tier.label}</h2>
                </div>
                <span className={online ? "text-sm font-semibold text-admin-good" : "text-sm font-semibold text-admin-muted"}>{online ? "Online" : "Offline"}</span>
              </div>
              {tier.agentName ? (
                <p className="mt-2 text-xs text-admin-muted">
                  Reachability from agent <span className="font-semibold">{tier.agentName}</span>
                  {agent ? ` — last beat ${formatSince(agent.secondsSinceSeen)}` : " — never reported"}
                </p>
              ) : null}
              <p className="mt-4 break-all text-sm text-admin-muted">{tier.basePath}</p>
              {tier.notes ? <p className="mt-3 text-sm text-admin-muted">{tier.notes}</p> : null}
              <div className="mt-5 grid grid-cols-2 gap-3 text-sm">
                <div className="rounded-md border border-admin-line bg-admin-subpanel p-3">
                  <p className="text-admin-muted">Movie files</p>
                  <p className="mt-1 text-xl font-semibold">{inventory?.movieFileCount ?? "0"}</p>
                </div>
                <div className="rounded-md border border-admin-line bg-admin-subpanel p-3">
                  <p className="text-admin-muted">Movie size</p>
                  <p className="mt-1 text-xl font-semibold">{inventory?.movieSize ?? "0 bytes"}</p>
                </div>
                <div className="rounded-md border border-admin-line bg-admin-subpanel p-3">
                  <p className="text-admin-muted">TV files</p>
                  <p className="mt-1 text-xl font-semibold">{inventory?.tvFileCount ?? "0"}</p>
                </div>
                <div className="rounded-md border border-admin-line bg-admin-subpanel p-3">
                  <p className="text-admin-muted">TV size</p>
                  <p className="mt-1 text-xl font-semibold">{inventory?.tvSize ?? "0 bytes"}</p>
                </div>
                <div className="rounded-md border border-admin-line bg-admin-subpanel p-3">
                  <p className="text-admin-muted">All files</p>
                  <p className="mt-1 text-xl font-semibold">{inventory?.fileCount ?? "0"}</p>
                </div>
                <div className="rounded-md border border-admin-line bg-admin-subpanel p-3">
                  <p className="text-admin-muted">All size</p>
                  <p className="mt-1 text-xl font-semibold">{inventory?.totalSize ?? "0 bytes"}</p>
                </div>
              </div>
              <div className="mt-5 space-y-2">
                <h3 className="text-sm font-semibold">Recent availability</h3>
                {events.length ? events.slice(0, 5).map((event) => (
                  <div key={event.id} className="rounded-md border border-admin-line bg-admin-subpanel p-3 text-sm">
                    <div className="flex justify-between gap-3">
                      <span>{event.online ? "Online" : "Offline"}</span>
                      <span className="text-admin-muted">{formatDate(event.detectedAt)}</span>
                    </div>
                    <p className="mt-1 text-admin-muted">Resolved: {formatDate(event.resolvedAt)}</p>
                  </div>
                )) : <p className="text-sm text-admin-muted">No availability observations yet.</p>}
              </div>
            </section>
          );
        }) : <section className="admin-panel p-5 text-sm text-admin-muted">No storage tiers configured yet.</section>}
      </div>
    </div>
  );
}
