/**
 * @fileoverview V3 Workflows Data Layer Composable
 * @description Provides V3 workflows data management for Sidepanel UI
 *
 * This composable wraps the V3 RPC client and provides:
 * - Flow listing, running, and deletion
 * - Run listing and event subscription
 * - Trigger management
 * - Data mapping from V3 types to UI types
 */

import {
  onMounted,
  onUnmounted,
  ref,
  type Ref,
} from "@/entrypoints/shared/reactivity";

import type { FlowV3 } from "@/entrypoints/background/record-replay-v3/domain/flow";
import type { RunRecordV3 } from "@/entrypoints/background/record-replay-v3/domain/events";
import type {
  TriggerKind,
  TriggerSpec,
} from "@/entrypoints/background/record-replay-v3/domain/triggers";
import type {
  FlowId,
  RunId,
  TriggerId,
} from "@/entrypoints/background/record-replay-v3/domain/ids";
import type { JsonObject } from "@/entrypoints/background/record-replay-v3/domain/json";
import { useRRV3Rpc } from "./useRRV3Rpc";
import {
  getActiveCurrentWindowTab,
  getActiveCurrentWindowTabId,
} from "@/entrypoints/shared/utils";

// ==================== UI Types ====================

/** Flow type for UI display (compatible with existing WorkflowsView) */
export interface FlowLite {
  id: string;
  name: string;
  description?: string;
  meta?: {
    domain?: string;
    tags?: string[];
    bindings?: Array<{
      kind?: string; // V3 uses 'kind'
      type?: string; // V2 uses 'type'
      value: string;
    }>;
  };
}

/** Run type for UI display (compatible with existing WorkflowsView) */
export interface RunLite {
  id: string;
  flowId: string;
  startedAt: string;
  finishedAt?: string;
  /**
   * Terminal success status: true=succeeded, false=failed/canceled, undefined=in progress
   * UI should check `isInProgress` first to distinguish in-progress from failed
   */
  success?: boolean;
  /** Whether the run is still in progress (queued/running/paused) */
  isInProgress: boolean;
  status: RunRecordV3["status"];
  entries: unknown[];
}

export type TriggerDraft = {
  id?: string;
  kind: TriggerKind;
  enabled: boolean;
  flowId: string;
  args?: JsonObject;
  match?: Array<{ kind: "url" | "domain" | "path"; value: string }>;
  periodMinutes?: number;
  whenMs?: number;
  commandKey?: string;
  title?: string;
  contexts?: string[];
  selector?: string;
  appear?: boolean;
  once?: boolean;
  debounceMs?: number;
};

// ==================== Mappers ====================

/** Convert V3 FlowV3 to UI FlowLite */
function mapFlowV3ToLite(flow: FlowV3): FlowLite {
  const domainBinding = flow.meta?.bindings?.find(
    (binding) => binding.kind === "domain",
  );
  return {
    id: flow.id,
    name: flow.name,
    description: flow.description,
    meta: {
      domain: flow.meta?.domain || domainBinding?.value,
      tags: flow.meta?.tags,
      bindings: flow.meta?.bindings?.map((b) => ({
        kind: b.kind,
        type: b.kind, // For V2 compatibility
        value: b.value,
      })),
    },
  };
}

/** Convert V3 RunRecordV3 to UI RunLite */
function mapRunV3ToLite(run: RunRecordV3): RunLite {
  // Determine if run is in progress
  const inProgressStatuses = ["queued", "running", "paused"];
  const isInProgress = inProgressStatuses.includes(run.status);

  // Map V3 status to success boolean for terminal states only
  let success: boolean | undefined;
  if (run.status === "succeeded") success = true;
  else if (run.status === "failed" || run.status === "canceled")
    success = false;
  // For in-progress states, success remains undefined

  return {
    id: run.id,
    flowId: run.flowId,
    startedAt: run.startedAt
      ? new Date(run.startedAt).toISOString()
      : new Date(run.createdAt).toISOString(),
    finishedAt: run.finishedAt
      ? new Date(run.finishedAt).toISOString()
      : undefined,
    success,
    isInProgress,
    status: run.status,
    entries: [], // V3 doesn't have entries in RunRecord, use getEvents for details
  };
}

async function getActiveTriggerContext(): Promise<{
  sourceTabId?: number;
  sourceUrl?: string;
}> {
  const tab = await getActiveCurrentWindowTab();
  const sourceTabId =
    typeof tab?.id === "number" && Number.isFinite(tab.id)
      ? Math.floor(tab.id)
      : undefined;
  const sourceUrl = typeof tab?.url === "string" ? tab.url : undefined;

  return {
    ...(sourceTabId !== undefined ? { sourceTabId } : {}),
    ...(sourceUrl ? { sourceUrl } : {}),
  };
}

// ==================== Composable ====================

export interface UseWorkflowsV3Options {
  /** Auto-refresh interval in ms (0 = disabled) */
  autoRefreshMs?: number;
  /** Auto-connect on mount */
  autoConnect?: boolean;
}

export interface UseWorkflowsV3Return {
  // Connection state
  connected: Ref<boolean>;
  loading: Ref<boolean>;
  error: Ref<string | null>;

  // Data
  flows: Ref<FlowLite[]>;
  runs: Ref<RunLite[]>;
  triggers: Ref<TriggerSpec[]>;

  // Actions
  refresh: () => Promise<void>;
  refreshFlows: () => Promise<void>;
  refreshRuns: () => Promise<void>;
  refreshTriggers: (flowId?: string) => Promise<void>;
  runFlow: (flowId: string) => Promise<{ runId: string } | null>;
  deleteFlow: (flowId: string) => Promise<boolean>;
  exportFlow: (flowId: string) => Promise<FlowV3 | null>;

  // V3-specific
  getFlowById: (flowId: string) => Promise<FlowV3 | null>;
  getRunEvents: (runId: string) => Promise<unknown[]>;
  createTrigger: (trigger: TriggerDraft) => Promise<TriggerSpec | null>;
  updateTrigger: (trigger: TriggerDraft & { id: string }) => Promise<TriggerSpec | null>;
  deleteTrigger: (triggerId: string) => Promise<boolean>;
  setTriggerEnabled: (triggerId: string, enabled: boolean) => Promise<boolean>;
  fireTrigger: (triggerId: string) => Promise<{ runId: string } | null>;
}

/**
 * V3 Workflows data layer composable
 */
export function useWorkflowsV3(
  options: UseWorkflowsV3Options = {},
): UseWorkflowsV3Return {
  const { autoRefreshMs = 0, autoConnect = true } = options;

  // RPC client
  const rpc = useRRV3Rpc({ autoConnect });

  // State
  const loading = ref(false);
  const error = ref<string | null>(null);
  const flows = ref<FlowLite[]>([]);
  const runs = ref<RunLite[]>([]);
  const triggers = ref<TriggerSpec[]>([]);

  // Auto-refresh timer
  let refreshTimer: ReturnType<typeof setInterval> | null = null;
  // Event subscription cleanup function
  let eventUnsubscribe: (() => void) | null = null;

  // ==================== Actions ====================

  async function refreshFlows(): Promise<void> {
    try {
      const result = (await rpc.request("rr_v3.listFlows")) as FlowV3[] | null;
      flows.value = (result || []).map(mapFlowV3ToLite);
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to refresh flows:", e);
      error.value = e instanceof Error ? e.message : String(e);
    }
  }

  async function refreshRuns(): Promise<void> {
    try {
      const result = (await rpc.request("rr_v3.listRuns")) as
        | RunRecordV3[]
        | null;
      // Sort by createdAt descending (newest first)
      const sorted = (result || [])
        .slice()
        .sort((a, b) => b.createdAt - a.createdAt);
      runs.value = sorted.map(mapRunV3ToLite);
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to refresh runs:", e);
      error.value = e instanceof Error ? e.message : String(e);
    }
  }

  async function refreshTriggers(flowId?: string): Promise<void> {
    try {
      const result = (await rpc.request("rr_v3.listTriggers", {
        ...(flowId ? { flowId: flowId as FlowId } : {}),
      })) as TriggerSpec[] | null;
      triggers.value = result || [];
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to refresh triggers:", e);
      error.value = e instanceof Error ? e.message : String(e);
    }
  }

  async function refresh(): Promise<void> {
    loading.value = true;
    error.value = null;
    try {
      await Promise.all([refreshFlows(), refreshRuns(), refreshTriggers()]);
    } finally {
      loading.value = false;
    }
  }

  async function runFlow(flowId: string): Promise<{ runId: string } | null> {
    try {
      const tabId = await getActiveCurrentWindowTabId();
      const result = (await rpc.request("rr_v3.enqueueRun", {
        flowId: flowId as FlowId,
        ...(tabId !== undefined ? { tabId } : {}),
        tabTarget: "current",
      })) as { runId: RunId; position: number } | null;
      // Refresh runs to show the new run
      void refreshRuns();
      return result ? { runId: result.runId } : null;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to run flow:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return null;
    }
  }

  async function deleteFlow(flowId: string): Promise<boolean> {
    try {
      await rpc.request("rr_v3.deleteFlow", { flowId: flowId as FlowId });
      // Refresh flows after deletion
      void refreshFlows();
      return true;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to delete flow:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return false;
    }
  }

  async function exportFlow(flowId: string): Promise<FlowV3 | null> {
    try {
      const result = (await rpc.request("rr_v3.getFlow", {
        flowId: flowId as FlowId,
      })) as FlowV3 | null;
      return result;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to export flow:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return null;
    }
  }

  async function getFlowById(flowId: string): Promise<FlowV3 | null> {
    try {
      return (await rpc.request("rr_v3.getFlow", {
        flowId: flowId as FlowId,
      })) as FlowV3 | null;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to get flow:", e);
      return null;
    }
  }

  async function getRunEvents(runId: string): Promise<unknown[]> {
    try {
      return (await rpc.request("rr_v3.getEvents", {
        runId: runId as RunId,
      })) as unknown[];
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to get run events:", e);
      return [];
    }
  }

  async function createTrigger(trigger: TriggerDraft): Promise<TriggerSpec | null> {
    try {
      const result = (await rpc.request("rr_v3.createTrigger", {
        trigger: trigger as unknown as JsonObject,
      })) as TriggerSpec | null;
      void refreshTriggers();
      return result;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to create trigger:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return null;
    }
  }

  async function updateTrigger(trigger: TriggerDraft & { id: string }): Promise<TriggerSpec | null> {
    try {
      const result = (await rpc.request("rr_v3.updateTrigger", {
        trigger: trigger as unknown as JsonObject,
      })) as TriggerSpec | null;
      void refreshTriggers();
      return result;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to update trigger:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return null;
    }
  }

  async function deleteTrigger(triggerId: string): Promise<boolean> {
    try {
      await rpc.request("rr_v3.deleteTrigger", {
        triggerId: triggerId as TriggerId,
      });
      void refreshTriggers();
      return true;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to delete trigger:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return false;
    }
  }

  async function setTriggerEnabled(triggerId: string, enabled: boolean): Promise<boolean> {
    try {
      await rpc.request(enabled ? "rr_v3.enableTrigger" : "rr_v3.disableTrigger", {
        triggerId: triggerId as TriggerId,
      });
      void refreshTriggers();
      return true;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to update trigger state:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return false;
    }
  }

  async function fireTrigger(triggerId: string): Promise<{ runId: string } | null> {
    try {
      const triggerContext = await getActiveTriggerContext();
      const result = (await rpc.request("rr_v3.fireTrigger", {
        triggerId: triggerId as TriggerId,
        ...triggerContext,
      })) as { runId: RunId } | null;
      void refreshRuns();
      return result ? { runId: result.runId } : null;
    } catch (e) {
      console.warn("[useWorkflowsV3] Failed to fire trigger:", e);
      error.value = e instanceof Error ? e.message : String(e);
      return null;
    }
  }

  // ==================== Lifecycle ====================

  onMounted(async () => {
    if (autoConnect) {
      await rpc.ensureConnected();
      await refresh();
    }

    // Setup auto-refresh
    if (autoRefreshMs > 0) {
      refreshTimer = setInterval(() => {
        void refresh();
      }, autoRefreshMs);
    }

    // Subscribe to all run events for real-time updates
    void rpc.subscribe(null);
    eventUnsubscribe = rpc.onEvent((event) => {
      // Refresh runs when run status changes
      const runStatusEvents = [
        "run.queued",
        "run.started",
        "run.succeeded",
        "run.failed",
        "run.canceled",
        "run.paused",
        "run.resumed",
        "run.recovered",
      ];
      if (runStatusEvents.includes(event.type)) {
        void refreshRuns();
      }
    });
  });

  onUnmounted(() => {
    // Cleanup auto-refresh timer
    if (refreshTimer) {
      clearInterval(refreshTimer);
      refreshTimer = null;
    }
    // Cleanup event subscription
    if (eventUnsubscribe) {
      eventUnsubscribe();
      eventUnsubscribe = null;
    }
    // Unsubscribe from run events
    void rpc.unsubscribe(null);
  });

  return {
    connected: rpc.connected,
    loading,
    error,
    flows,
    runs,
    triggers,
    refresh,
    refreshFlows,
    refreshRuns,
    refreshTriggers,
    runFlow,
    deleteFlow,
    exportFlow,
    getFlowById,
    getRunEvents,
    createTrigger,
    updateTrigger,
    deleteTrigger,
    setTriggerEnabled,
    fireTrigger,
  };
}
