'use client';

import { useCallback, useEffect, useMemo, useState } from 'react';

import { RoleGate } from '@/components/auth/RoleGate';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { IT_OPS_ALLOWED_ROLES } from '@/lib/it-ops-roles';

type DriftShape = {
  collected_at?: string;
  guest_count?: number;
  duplicate_ips?: Record<string, string[]>;
  same_name_duplicate_ip_guests?: Record<string, string[]>;
  guest_lan_ips_not_in_declared_sources?: string[];
  declared_lan11_ips_not_on_live_guests?: string[];
  vmid_ip_mismatch_live_vs_all_vmids_doc?: Array<{ vmid: string; live_ip: string; all_vmids_doc_ip: string }>;
  vmids_in_all_vmids_doc_not_on_cluster?: string[];
  vmids_on_cluster_not_in_all_vmids_table?: {
    count?: number;
    sample_vmids?: string[];
    note?: string;
  };
  notes?: string[];
};

type SummaryShape = {
  envelope_at?: string;
  declared_git_head?: string | null;
  live_collected_at?: string;
  drift_collected_at?: string;
  guest_count?: number | null;
  duplicate_ip_bucket_count?: number;
  seed_unreachable?: boolean;
  drift_notes?: string[];
  vmids_in_all_vmids_doc_not_on_cluster?: string[];
  vmids_on_cluster_not_in_all_vmids_table?: DriftShape['vmids_on_cluster_not_in_all_vmids_table'];
  artifacts?: {
    live_inventory?: { mtime_utc?: string; exists?: boolean };
    drift?: { mtime_utc?: string; exists?: boolean };
  };
};

type PortmapShape = {
  implementation?: string;
  stale?: boolean;
  note?: string;
  rows?: unknown[];
};

function hoursSinceIso(iso: string | undefined): number | null {
  if (!iso) return null;
  const t = Date.parse(iso);
  if (Number.isNaN(t)) return null;
  return (Date.now() - t) / (1000 * 60 * 60);
}

export default function ItOpsPage() {
  const [drift, setDrift] = useState<DriftShape | null>(null);
  const [summary, setSummary] = useState<SummaryShape | null>(null);
  const [portmap, setPortmap] = useState<PortmapShape | null>(null);
  const [err, setErr] = useState<string | null>(null);
  const [refreshWarn, setRefreshWarn] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [refreshing, setRefreshing] = useState(false);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const [driftRes, summaryRes, portmapRes] = await Promise.all([
        fetch('/api/it/drift', { cache: 'no-store' }),
        fetch('/api/it/summary', { cache: 'no-store' }),
        fetch('/api/it/portmap', { cache: 'no-store' }),
      ]);
      const dj = (await driftRes.json()) as DriftShape & { message?: string };
      if (!driftRes.ok) {
        setErr(dj.message || `HTTP ${driftRes.status}`);
        setDrift(null);
        setSummary(null);
        setPortmap(null);
        setRefreshWarn(null);
        return;
      }
      setDrift(dj);
      const sj = (await summaryRes.json()) as SummaryShape & { message?: string };
      setSummary(summaryRes.ok ? sj : null);
      const pj = (await portmapRes.json()) as PortmapShape & { message?: string };
      setPortmap(portmapRes.ok ? pj : null);
    } catch (e) {
      setErr(e instanceof Error ? e.message : 'Request failed');
      setDrift(null);
      setSummary(null);
      setPortmap(null);
      setRefreshWarn(null);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    void load();
  }, [load]);

  const staleHours = useMemo(() => hoursSinceIso(drift?.collected_at), [drift?.collected_at]);
  const stale = staleHours !== null && staleHours > 24;

  const onRefresh = async () => {
    setRefreshing(true);
    setErr(null);
    setRefreshWarn(null);
    try {
      const r = await fetch('/api/it/refresh', { method: 'POST' });
      const j = (await r.json()) as {
        message?: string;
        drift_exit_code?: number;
        duplicate_guest_ip_conflict?: boolean;
      };
      if (!r.ok) {
        setErr(j.message || `Refresh HTTP ${r.status}`);
        setRefreshing(false);
        return;
      }
      if (j.drift_exit_code === 2 || j.duplicate_guest_ip_conflict) {
        setRefreshWarn(
          'Refresh completed but the exporter reported duplicate LAN IPs on different guest names (drift exit 2). Resolve conflicts on the cluster.',
        );
      }
      await load();
    } catch (e) {
      setErr(e instanceof Error ? e.message : 'Refresh failed');
    } finally {
      setRefreshing(false);
    }
  };

  const dupCount = drift?.duplicate_ips ? Object.keys(drift.duplicate_ips).length : 0;
  const sameNameDupCount = drift?.same_name_duplicate_ip_guests
    ? Object.keys(drift.same_name_duplicate_ip_guests).length
    : 0;
  const docMissingVmidCount = drift?.vmids_in_all_vmids_doc_not_on_cluster?.length ?? 0;
  const liveExtraTable = drift?.vmids_on_cluster_not_in_all_vmids_table;

  return (
    <RoleGate
      allowedRoles={[...IT_OPS_ALLOWED_ROLES]}
      callbackUrl="/it"
      badge="IT Ops"
      title="IT operations"
      subtitle="Sign in with an account that has the sankofa-it-admin realm role (or operator ADMIN for bootstrap)."
    >
      <div className="container mx-auto px-4 py-8">
        <div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <h1 className="text-3xl font-bold text-white">IT inventory &amp; drift</h1>
            <p className="text-gray-400 text-sm mt-1">
              Data from proxmox read API (Phase 0). Configure{' '}
              <code className="text-gray-300">IT_READ_API_URL</code> on the portal host.
            </p>
          </div>
          <button
            type="button"
            onClick={() => void onRefresh()}
            disabled={refreshing}
            className="inline-flex h-10 items-center justify-center rounded-md bg-orange-600 px-4 text-sm font-medium text-white hover:bg-orange-500 disabled:opacity-50"
          >
            {refreshing ? 'Refreshing…' : 'Refresh inventory'}
          </button>
        </div>

        {err && (
          <div className="mb-6 rounded-lg border border-red-800 bg-red-950/40 px-4 py-3 text-sm text-red-200">
            {err}
          </div>
        )}

        {refreshWarn && (
          <div className="mb-6 rounded-lg border border-amber-700 bg-amber-950/30 px-4 py-3 text-sm text-amber-100">
            {refreshWarn}
          </div>
        )}

        {loading && <p className="text-gray-400">Loading drift…</p>}

        {!loading && drift && (
          <div className="grid gap-6 md:grid-cols-2">
            <Card className="bg-gray-800 border-gray-700">
              <CardHeader>
                <CardTitle className="text-white">Freshness</CardTitle>
              </CardHeader>
              <CardContent className="space-y-2 text-sm text-gray-300">
                <p>
                  <span className="text-gray-500">collected_at:</span>{' '}
                  {drift.collected_at || '—'}
                </p>
                {stale && (
                  <p className="text-amber-400">
                    Snapshot is older than 24h — run export on LAN or use Refresh (requires API key on server).
                  </p>
                )}
                {!stale && staleHours !== null && (
                  <p className="text-green-400">Within 24h window ({Math.round(staleHours)}h ago).</p>
                )}
              </CardContent>
            </Card>

            <Card className="bg-gray-800 border-gray-700">
              <CardHeader>
                <CardTitle className="text-white">Summary</CardTitle>
              </CardHeader>
              <CardContent className="space-y-2 text-sm text-gray-300">
                <p>Guests (live): {drift.guest_count ?? '—'}</p>
                <p>Duplicate guest IPs (different names): {dupCount}</p>
                <p>Same-name IP clones (informational): {sameNameDupCount}</p>
                <p>
                  LAN guests not in declared sources:{' '}
                  {drift.guest_lan_ips_not_in_declared_sources?.length ?? 0}
                </p>
                <p>
                  Declared LAN11 not on live guests:{' '}
                  {drift.declared_lan11_ips_not_on_live_guests?.length ?? 0}
                </p>
                <p>
                  VMID IP mismatch (live vs ALL_VMIDS doc):{' '}
                  {drift.vmid_ip_mismatch_live_vs_all_vmids_doc?.length ?? 0}
                </p>
              </CardContent>
            </Card>

            {summary && (
              <Card className="bg-gray-800 border-gray-700 md:col-span-2">
                <CardHeader>
                  <CardTitle className="text-white">API envelope</CardTitle>
                </CardHeader>
                <CardContent className="space-y-2 text-sm text-gray-300">
                  {summary.seed_unreachable && (
                    <p className="text-amber-400">
                      Seed host unreachable from exporter — drift may be stubbed. Run export on LAN.
                    </p>
                  )}
                  <p>
                    <span className="text-gray-500">live_inventory.json mtime:</span>{' '}
                    {summary.artifacts?.live_inventory?.mtime_utc ?? '—'}
                  </p>
                  <p>
                    <span className="text-gray-500">drift.json mtime:</span>{' '}
                    {summary.artifacts?.drift?.mtime_utc ?? '—'}
                  </p>
                  <p>
                    <span className="text-gray-500">envelope_at:</span> {summary.envelope_at ?? '—'}
                  </p>
                  <p>
                    <span className="text-gray-500">declared_git_head (repo on API host):</span>{' '}
                    {summary.declared_git_head ?? '—'}
                  </p>
                </CardContent>
              </Card>
            )}

            <Card className="bg-gray-800 border-gray-700 md:col-span-2">
              <CardHeader>
                <CardTitle className="text-white">VLAN plan</CardTitle>
              </CardHeader>
              <CardContent className="space-y-2 text-sm text-gray-300">
                <p>
                  <span className="text-gray-500">Current:</span> flat{' '}
                  <code className="text-gray-200">192.168.11.0/24</code> (VLAN 11).
                </p>
                <p>
                  <span className="text-gray-500">Target:</span> segmented VLANs (110–112, 120, 160, 200–203) in
                  repo doc <code className="text-gray-200">docs/11-references/NETWORK_CONFIGURATION_MASTER.md</code>{' '}
                  (section VLAN Configuration).
                </p>
                <p>
                  Operator runbook:{' '}
                  <code className="text-gray-200">docs/03-deployment/VLAN_FLAT_11_TO_SEGMENTED_RUNBOOK.md</code> and{' '}
                  <code className="text-gray-200">scripts/it-ops/vlan-segmentation-ordered-checklist.sh</code>.
                </p>
              </CardContent>
            </Card>

            <Card className="bg-gray-800 border-gray-700 md:col-span-2">
              <CardHeader>
                <CardTitle className="text-white">Hardware &amp; cluster reports</CardTitle>
              </CardHeader>
              <CardContent className="space-y-2 text-sm text-gray-300">
                <p>
                  Poll script (LAN):{' '}
                  <code className="text-gray-200">bash scripts/verify/poll-proxmox-cluster-hardware.sh</code>
                </p>
                <p>
                  Artifacts under <code className="text-gray-200">reports/status/</code>:{' '}
                  <code className="text-gray-200">hardware_poll_*.txt</code>,{' '}
                  <code className="text-gray-200">hardware_and_connected_inventory_*.md</code>.
                </p>
                <p>
                  Edge / discovery IPs:{' '}
                  <code className="text-gray-200">docs/04-configuration/IT_OPS_EDGE_DISCOVERY_IPS.md</code>
                </p>
              </CardContent>
            </Card>

            {drift && (docMissingVmidCount > 0 || (liveExtraTable?.count ?? 0) > 0) && (
              <Card className="bg-gray-800 border-gray-700 md:col-span-2">
                <CardHeader>
                  <CardTitle className="text-white">VMID coverage (live vs ALL_VMIDS tables)</CardTitle>
                </CardHeader>
                <CardContent className="space-y-3 text-sm text-gray-300">
                  <p>
                    VMIDs in ALL_VMIDS doc but not on cluster:{' '}
                    <span className="text-amber-300">{docMissingVmidCount}</span>
                    {docMissingVmidCount > 0 && (
                      <pre className="mt-2 text-xs text-gray-400 overflow-x-auto max-h-32">
                        {JSON.stringify(drift.vmids_in_all_vmids_doc_not_on_cluster, null, 2)}
                      </pre>
                    )}
                  </p>
                  <p>
                    Guests on cluster not listed in ALL_VMIDS pipe tables:{' '}
                    <span className="text-gray-200">{liveExtraTable?.count ?? 0}</span>
                    {liveExtraTable?.note && (
                      <span className="block text-gray-500 mt-1">{liveExtraTable.note}</span>
                    )}
                  </p>
                  {(liveExtraTable?.sample_vmids?.length ?? 0) > 0 && (
                    <pre className="text-xs text-gray-400 overflow-x-auto max-h-32">
                      {JSON.stringify(liveExtraTable?.sample_vmids, null, 2)}
                    </pre>
                  )}
                </CardContent>
              </Card>
            )}

            {portmap && (
              <Card className="bg-gray-800 border-gray-700 md:col-span-2">
                <CardHeader>
                  <CardTitle className="text-white">Port map (joined view)</CardTitle>
                </CardHeader>
                <CardContent className="space-y-2 text-sm text-gray-300">
                  <p>
                    Status:{' '}
                    <span className={portmap.stale ? 'text-amber-400' : 'text-green-400'}>
                      {portmap.implementation ?? 'unknown'}
                      {portmap.stale ? ' (stale / stub)' : ''}
                    </span>
                  </p>
                  {portmap.note && <p className="text-gray-400">{portmap.note}</p>}
                </CardContent>
              </Card>
            )}

            {dupCount > 0 && (
              <Card className="bg-gray-800 border-red-900 md:col-span-2">
                <CardHeader>
                  <CardTitle className="text-red-400">Duplicate IPs — different guest names</CardTitle>
                </CardHeader>
                <CardContent>
                  <pre className="text-xs text-gray-300 overflow-x-auto">
                    {JSON.stringify(drift.duplicate_ips, null, 2)}
                  </pre>
                </CardContent>
              </Card>
            )}

            {sameNameDupCount > 0 && (
              <Card className="bg-gray-800 border-amber-900/60 md:col-span-2">
                <CardHeader>
                  <CardTitle className="text-amber-200">Same-name guests sharing an IP</CardTitle>
                </CardHeader>
                <CardContent className="space-y-2 text-sm text-gray-300">
                  <p>
                    Multiple VMIDs use the same IP and identical hostname (e.g. clones). Resolve in Proxmox;
                    export no longer fails CI on this alone.
                  </p>
                  <pre className="text-xs text-gray-300 overflow-x-auto">
                    {JSON.stringify(drift.same_name_duplicate_ip_guests, null, 2)}
                  </pre>
                </CardContent>
              </Card>
            )}

            {(drift.notes?.length ?? 0) > 0 && (
              <Card className="bg-gray-800 border-gray-700 md:col-span-2">
                <CardHeader>
                  <CardTitle className="text-white">Notes</CardTitle>
                </CardHeader>
                <CardContent>
                  <ul className="list-disc pl-5 text-sm text-gray-300 space-y-1">
                    {drift.notes!.map((n) => (
                      <li key={n}>{n}</li>
                    ))}
                  </ul>
                </CardContent>
              </Card>
            )}
          </div>
        )}
      </div>
    </RoleGate>
  );
}
