import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { useWebSocket } from '../hooks/useWebSocket';
import { formatHashrate } from '../help/fleetFilters';
import './ActivityFeedPage.css';

// ── Event types ───────────────────────────────────────────────────────────

export type ActivityEventKind =
  | 'connect'
  | 'disconnect'
  | 'hashrate'
  | 'share'
  | 'alert'
  | 'command'
  | 'ai'
  | 'posture'
  | 'default';

export interface ActivityEvent {
  id: string;
  kind: ActivityEventKind;
  agentId?: string;
  agentName?: string;
  message: string;
  detail?: string;
  ts: Date;
  raw?: unknown;
}

let _eid = 0;
function eid() { return String(++_eid); }

// ── Visual config per kind ────────────────────────────────────────────────

const KIND_CONFIG: Record<ActivityEventKind, { icon: string; label: string; color: string }> = {
  connect:    { icon: '🟢', label: 'ONLINE',    color: '#39ff14' },
  disconnect: { icon: '🔴', label: 'OFFLINE',   color: '#ff4444' },
  hashrate:   { icon: '⚡',  label: 'HASHRATE',  color: '#00e8f5' },
  share:      { icon: '✅',  label: 'SHARE',     color: '#b24bf3' },
  alert:      { icon: '⚠️',  label: 'ALERT',     color: '#ff6b35' },
  command:    { icon: '📡',  label: 'COMMAND',   color: '#ffb020' },
  ai:         { icon: '🤖',  label: 'AI',        color: '#ff2da6' },
  posture:    { icon: '🛡️',  label: 'POSTURE',   color: '#a8ff78' },
  default:    { icon: '·',   label: 'EVENT',     color: '#8899aa' },
};

// ALL_KINDS excludes 'default' — that kind is a fallback sentinel and is never
// actually emitted, so it would only create a permanently-zero filter chip.
const ALL_KINDS = (Object.keys(KIND_CONFIG) as ActivityEventKind[]).filter(
  (k) => k !== 'default'
);
const MAX_EVENTS = 500;

function fmt(d: Date): string {
  return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}

// ── Event row ─────────────────────────────────────────────────────────────

function EventRow({ event }: { event: ActivityEvent }) {
  const cfg = KIND_CONFIG[event.kind];
  return (
    <div className="activity-event">
      <div className={`activity-event-accent accent--${event.kind}`} />
      <span className="activity-event-icon">{cfg.icon}</span>
      <div className="activity-event-body">
        <div className="activity-event-main">
          <span className={`activity-event-type-badge badge--${event.kind}`}>{cfg.label}</span>
          {event.agentName && (
            <span className="activity-event-agent" title={event.agentId}>
              {event.agentName}
            </span>
          )}
          <span className="activity-event-msg">{event.message}</span>
        </div>
        {event.detail && (
          <div className="activity-event-detail">{event.detail}</div>
        )}
      </div>
      <span className="activity-event-ts">{fmt(event.ts)}</span>
    </div>
  );
}

// ── Main page ─────────────────────────────────────────────────────────────

export default function ActivityFeedPage() {
  const {
    isConnected,
    agents,
    recentShares,
    fleetAlerts,
    commandResults,
    aiActivity,
  } = useWebSocket();

  const [events, setEvents] = useState<ActivityEvent[]>([]);
  const [activeFilters, setActiveFilters] = useState<Set<ActivityEventKind>>(new Set(ALL_KINDS));
  const [search, setSearch] = useState('');
  const [autoScroll, setAutoScroll] = useState(true);
  const streamRef = useRef<HTMLDivElement>(null);
  const agentMapRef = useRef<Map<string, string>>(new Map()); // id → name
  const prevAgentStatus = useRef<Record<string, string>>({}); // id → status
  const prevHashrates = useRef<Record<string, number>>({}); // id → hashrate_15m
  const prevPosture = useRef<Record<string, number>>({}); // id → posture_score

  // Build agent name lookup
  useEffect(() => {
    for (const a of agents) agentMapRef.current.set(a.id, a.name);
  }, [agents]);

  const push = useCallback((ev: ActivityEvent) => {
    setEvents((prev) => [ev, ...prev].slice(0, MAX_EVENTS));
  }, []);

  // ── Agent status change events (online / offline) ──────────────────────
  useEffect(() => {
    for (const agent of agents) {
      const prev = prevAgentStatus.current[agent.id];
      if (prev === undefined) {
        // First time we see this agent — synthetic "connect" on page load
        prevAgentStatus.current[agent.id] = agent.status;
        if (agent.status === 'online') {
          push({
            id: eid(), kind: 'connect',
            agentId: agent.id, agentName: agent.name,
            message: 'came online',
            detail: `${agent.platform ?? 'unknown'} · ${agent.ip ?? '—'} · ${agent.cpu_cores}c`,
            ts: new Date(),
          });
        }
        continue;
      }
      if (prev !== agent.status) {
        prevAgentStatus.current[agent.id] = agent.status;
        if (agent.status === 'online') {
          push({
            id: eid(), kind: 'connect',
            agentId: agent.id, agentName: agent.name,
            message: 'reconnected',
            detail: `${agent.platform ?? ''} · ${agent.ip ?? '—'}`,
            ts: new Date(),
          });
        } else {
          push({
            id: eid(), kind: 'disconnect',
            agentId: agent.id, agentName: agent.name,
            message: 'went offline',
            ts: new Date(),
          });
        }
      }
    }
  }, [agents, push]);

  // ── Hashrate spike events ──────────────────────────────────────────────
  useEffect(() => {
    for (const agent of agents) {
      if (agent.status !== 'online') continue;
      const prev = prevHashrates.current[agent.id];
      const cur = agent.hashrate_15m ?? 0;
      prevHashrates.current[agent.id] = cur;
      if (prev === undefined || prev <= 0) continue;
      const delta = cur - prev;
      // Only emit if ≥20% change AND at least 100 H/s delta
      if (Math.abs(delta) >= 100 && Math.abs(delta) / Math.max(prev, 1) >= 0.20) {
        push({
          id: eid(), kind: 'hashrate',
          agentId: agent.id, agentName: agent.name,
          message: delta > 0 ? `hashrate up to ${formatHashrate(cur)}` : `hashrate dropped to ${formatHashrate(cur)}`,
          detail: `Δ${delta > 0 ? '+' : ''}${formatHashrate(delta)}`,
          ts: new Date(),
        });
      }
    }
  }, [agents, push]);

  // ── Posture score change events ────────────────────────────────────────
  useEffect(() => {
    for (const agent of agents) {
      if (agent.posture_score == null) continue;
      const prev = prevPosture.current[agent.id];
      const cur = agent.posture_score;
      prevPosture.current[agent.id] = cur;
      if (prev === undefined) continue;
      const delta = cur - prev;
      if (Math.abs(delta) >= 10) {
        push({
          id: eid(), kind: 'posture',
          agentId: agent.id, agentName: agent.name,
          message: `posture score ${delta > 0 ? 'improved' : 'degraded'} to ${cur}/100`,
          detail: `Δ${delta > 0 ? '+' : ''}${delta}`,
          ts: new Date(),
        });
      }
    }
  }, [agents, push]);

  // ── New share events ───────────────────────────────────────────────────
  const lastShareId = useRef<string | null>(null);
  useEffect(() => {
    if (recentShares.length === 0) return;
    const top = recentShares[0];
    const key = top.id != null ? String(top.id) : `${top.agent_id}-${top.hash}`;
    if (key === lastShareId.current) return;
    lastShareId.current = key;
    const name = agentMapRef.current.get(top.agent_id) ?? top.agent_id?.slice(0, 8);
    push({
      id: eid(), kind: 'share',
      agentId: top.agent_id, agentName: name,
      message: top.accepted ? 'share accepted by pool' : 'share rejected',
      detail: top.accepted ? undefined : top.error ?? 'pool rejection',
      ts: new Date(top.timestamp ?? Date.now()),
    });
  }, [recentShares, push]);

  // ── Fleet alert events ─────────────────────────────────────────────────
  const lastAlertId = useRef<string | null>(null);
  useEffect(() => {
    if (fleetAlerts.length === 0) return;
    const top = fleetAlerts[0];
    if (top.id === lastAlertId.current) return;
    lastAlertId.current = top.id;
    push({
      id: eid(), kind: 'alert',
      agentId: top.agent_id, agentName: top.agent_name,
      message: top.message,
      detail: top.type,
      ts: new Date(top.timestamp ?? Date.now()),
    });
  }, [fleetAlerts, push]);

  // ── Command result events ──────────────────────────────────────────────
  const lastCmdSeq = useRef(-1);
  useEffect(() => {
    if (commandResults.length === 0) return;
    const top = commandResults[commandResults.length - 1];
    if ((top._seq ?? -1) <= lastCmdSeq.current) return;
    lastCmdSeq.current = top._seq ?? -1;
    const name = agentMapRef.current.get(top.agent_id ?? '') ?? top.agent_id?.slice(0, 8);
    push({
      id: eid(), kind: 'command',
      agentId: top.agent_id, agentName: name,
      message: `${top.action} → ${top.success ? 'success' : 'failed'}`,
      detail: top.success ? undefined : top.message?.slice(0, 80),
      ts: new Date(),
    });
  }, [commandResults, push]);

  // ── AI activity events ─────────────────────────────────────────────────
  const lastAiAgent = useRef<Record<string, string>>({});
  useEffect(() => {
    for (const entry of aiActivity) {
      const lastAction = lastAiAgent.current[entry.agent_id];
      if (entry.last_action && entry.last_action !== lastAction) {
        lastAiAgent.current[entry.agent_id] = entry.last_action;
        const name = agentMapRef.current.get(entry.agent_id) ?? entry.agent_id?.slice(0, 8);
        push({
          id: eid(), kind: 'ai',
          agentId: entry.agent_id, agentName: name,
          message: `AI decided: ${entry.last_action}`,
          detail: entry.last_reasoning?.slice(0, 80),
          ts: entry.last_decide_at ? new Date(entry.last_decide_at) : new Date(),
        });
      }
    }
  }, [aiActivity, push]);

  // ── Auto-scroll ────────────────────────────────────────────────────────
  useEffect(() => {
    if (!autoScroll || !streamRef.current) return;
    streamRef.current.scrollTop = 0; // newest is at top
  }, [events, autoScroll]);

  // ── Filtered view ──────────────────────────────────────────────────────
  const filtered = useMemo(() => {
    let list = events.filter((e) => activeFilters.has(e.kind));
    if (search.trim()) {
      const q = search.trim().toLowerCase();
      list = list.filter((e) =>
        (e.agentName ?? '').toLowerCase().includes(q) ||
        e.message.toLowerCase().includes(q) ||
        (e.detail ?? '').toLowerCase().includes(q)
      );
    }
    return list;
  }, [events, activeFilters, search]);

  // ── Stats for pills ────────────────────────────────────────────────────
  const onlineCount = agents.filter((a) => a.status === 'online').length;
  const totalHashrate = agents.reduce((s, a) => s + (a.hashrate_15m ?? 0), 0);
  const alertCount = fleetAlerts.length;

  const toggleFilter = (kind: ActivityEventKind) => {
    setActiveFilters((prev) => {
      const next = new Set(prev);
      if (next.has(kind)) { next.delete(kind); } else { next.add(kind); }
      if (next.size === 0) return new Set(ALL_KINDS); // prevent empty
      return next;
    });
  };

  const countByKind = useMemo(() => {
    const m: Record<string, number> = {};
    for (const e of events) m[e.kind] = (m[e.kind] ?? 0) + 1;
    return m;
  }, [events]);

  return (
    <div className="page fade-in activity-page">

      {/* ── Hero ─────────────────────────────────────────────────────────── */}
      <header className="activity-hero">
        <div className="activity-hero-text">
          <p className="activity-eyebrow">REAL-TIME INTELLIGENCE</p>
          <h1>Activity Feed</h1>
          <p className="page-subtitle">
            Live event stream · agent connects · hashrate · shares · commands · AI decisions
          </p>
        </div>
        <div className="activity-live-badge">
          <div className={`activity-live-dot ${isConnected ? '' : 'offline'}`} />
          {isConnected ? 'LIVE' : 'DISCONNECTED'}
          {isConnected && <span style={{ color: 'rgba(57,255,20,0.6)' }}>· {events.length} events</span>}
        </div>
      </header>

      {/* ── Stats pills ──────────────────────────────────────────────────── */}
      <div className="activity-stat-pills">
        <div className="activity-stat-pill">
          <div className="activity-stat-pill-dot" style={{ background: '#39ff14', boxShadow: '0 0 4px #39ff14' }} />
          {onlineCount} / {agents.length} online
        </div>
        {totalHashrate > 0 && (
          <div className="activity-stat-pill">
            <div className="activity-stat-pill-dot" style={{ background: '#00e8f5' }} />
            {formatHashrate(totalHashrate)}
          </div>
        )}
        {alertCount > 0 && (
          <div className="activity-stat-pill">
            <div className="activity-stat-pill-dot" style={{ background: '#ff6b35' }} />
            {alertCount} alert{alertCount !== 1 ? 's' : ''}
          </div>
        )}
        <div className="activity-stat-pill" style={{ marginLeft: 'auto' }}>
          <input
            type="checkbox"
            id="autoscroll-check"
            checked={autoScroll}
            onChange={(e) => setAutoScroll(e.target.checked)}
            style={{ cursor: 'pointer', accentColor: '#00e8f5' }}
          />
          <label htmlFor="autoscroll-check" style={{ cursor: 'pointer', color: 'var(--text-muted)', fontSize: '0.62rem', fontFamily: 'monospace' }}>
            Auto-scroll
          </label>
        </div>
      </div>

      {/* ── Filter bar ───────────────────────────────────────────────────── */}
      <div className="activity-filters">
        <span className="activity-filter-label">FILTER:</span>
        {ALL_KINDS.map((kind) => {
          const cfg = KIND_CONFIG[kind];
          const isActive = activeFilters.has(kind);
          const count = countByKind[kind] ?? 0;
          return (
            <button
              key={kind}
              type="button"
              className={`activity-type-chip ${isActive ? `active--${kind}` : ''}`}
              onClick={() => toggleFilter(kind)}
              title={`${isActive ? 'Hide' : 'Show'} ${cfg.label} events`}
            >
              {cfg.icon} {cfg.label}
              {count > 0 && <span style={{ opacity: 0.65 }}> {count}</span>}
            </button>
          );
        })}
        <input
          className="activity-search"
          type="text"
          placeholder="Search agent, message…"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
        {(events.length > 0 || search) && (
          <button
            type="button"
            className="activity-clear-btn"
            onClick={() => { setEvents([]); setSearch(''); }}
          >
            CLEAR ALL
          </button>
        )}
      </div>

      {/* ── Event stream ─────────────────────────────────────────────────── */}
      <div className="activity-stream-wrap">
        <div className="activity-stream-header">
          <span>◆ LIVE EVENT STREAM</span>
          <span>sorted by most recent</span>
          <span className="activity-stream-count">
            {filtered.length} events{search ? ' matching' : ''}
          </span>
        </div>

        <div
          ref={streamRef}
          className="activity-stream"
          onScroll={(e) => {
            // Disable auto-scroll when user scrolls away from top
            const el = e.currentTarget;
            setAutoScroll(el.scrollTop < 60);
          }}
        >
          {filtered.length === 0 ? (
            <div className="activity-empty">
              <span className="activity-empty-icon">📡</span>
              {events.length === 0
                ? 'Waiting for fleet events…'
                : 'No events match your filters.'}
            </div>
          ) : (
            filtered.map((ev) => <EventRow key={ev.id} event={ev} />)
          )}
        </div>
      </div>
    </div>
  );
}
