/**
 * `useDrift` — hook that detects agent drift and exposes the `syncAgent` action.
 *
 * Used by `AgentsPanel` to show "update available" badges and Sync buttons for
 * synchronized agents whose template has been updated since they were last synced.
 */

import { useCallback, useEffect, useState } from "react";

import type { AgentDrift, GatewayError } from "@/domain";
import { useGateways } from "@/app/di";

export interface DriftViewModel {
  /** Drift entries keyed by agentId for O(1) lookup in the panel. */
  driftByAgentId: Map<string, AgentDrift>;
  /** Whether a drift-related request is in flight. */
  driftBusy: boolean;
  /** Last drift-related error message, or `null`. */
  driftError: string | null;
  /** Refreshes the drift list. */
  refreshDrift: () => Promise<void>;
  /**
   * Syncs the given agent to the current template version.
   * After syncing, calls `onSynced` (typically a refresh of the agent list)
   * and refreshes drift.
   */
  syncAgent: (agentId: string, onSynced?: () => Promise<void>) => Promise<void>;
}

function describe(e: unknown): string {
  if (e && typeof e === "object" && "message" in e) {
    return String((e as GatewayError).message);
  }
  return String(e);
}

export function useDrift(projectId: string): DriftViewModel {
  const { template } = useGateways();

  const [drifts, setDrifts] = useState<AgentDrift[]>([]);
  const [driftBusy, setDriftBusy] = useState(false);
  const [driftError, setDriftError] = useState<string | null>(null);

  const refreshDrift = useCallback(async () => {
    setDriftBusy(true);
    setDriftError(null);
    try {
      const list = await template.detectDrift(projectId);
      setDrifts(list);
    } catch (e) {
      setDriftError(describe(e));
    } finally {
      setDriftBusy(false);
    }
  }, [template, projectId]);

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

  const syncAgent = useCallback(
    async (agentId: string, onSynced?: () => Promise<void>) => {
      setDriftBusy(true);
      setDriftError(null);
      try {
        await template.syncAgent(projectId, agentId);
        if (onSynced) await onSynced();
        // Re-check drift after sync
        const list = await template.detectDrift(projectId);
        setDrifts(list);
      } catch (e) {
        setDriftError(describe(e));
      } finally {
        setDriftBusy(false);
      }
    },
    [template, projectId],
  );

  const driftByAgentId = new Map<string, AgentDrift>(
    drifts.map((d) => [d.agentId, d]),
  );

  return {
    driftByAgentId,
    driftBusy,
    driftError,
    refreshDrift,
    syncAgent,
  };
}
