/**
 * Automation Hooks
 *
 * React hooks for fetching and mutating automations via MCP tools.
 */

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMCPClient, useProjectContext, SELF_MCP_ALIAS_ID } from "@/sdk";
import { toast } from "sonner";
import { KEYS } from "../lib/query-keys";
import { useStudioTools } from "../lib/studio-tools";
import type { StudioToolInput as ToolInput } from "@decocms/shared/tools/tool-io";

// ============================================================================
// Trigger List Hook
// ============================================================================

export interface TriggerParamSchema {
  type: string;
  enum?: string[];
  description?: string;
}

export interface TriggerDefinition {
  type: string;
  description: string;
  paramsSchema: Record<string, TriggerParamSchema>;
}

/**
 * Raw trigger format from MCP servers (e.g. GitHub MCP uses `params` array)
 */
interface RawTriggerDefinition {
  type: string;
  description: string;
  paramsSchema?: Record<string, TriggerParamSchema>;
  params?:
    | Array<{
        name: string;
        type: string;
        description?: string;
        enum?: string[];
        required?: boolean;
      }>
    | Record<string, TriggerParamSchema>;
}

/**
 * Normalize trigger definitions from different MCP server formats.
 * Some servers return `paramsSchema` as a Record (per binding spec),
 * others return `params` as an array (e.g. GitHub MCP).
 */
function normalizeTrigger(raw: RawTriggerDefinition): TriggerDefinition {
  if (raw.paramsSchema && typeof raw.paramsSchema === "object") {
    return raw as TriggerDefinition;
  }

  const paramsSchema: Record<string, TriggerParamSchema> = {};

  if (Array.isArray(raw.params)) {
    for (const p of raw.params) {
      paramsSchema[p.name] = {
        type: p.type,
        description: p.description,
        enum: p.enum,
      };
    }
  } else if (raw.params && typeof raw.params === "object") {
    Object.assign(paramsSchema, raw.params);
  }

  return {
    type: raw.type,
    description: raw.description,
    paramsSchema,
  };
}

export function useTriggerList(connectionId: string | undefined) {
  const { org } = useProjectContext();
  const client = useMCPClient({
    connectionId: connectionId ?? SELF_MCP_ALIAS_ID,
    orgId: org.id,
    orgSlug: org.slug,
  });

  return useQuery({
    queryKey: KEYS.toolCall(connectionId ?? "", "TRIGGER_LIST", ""),
    queryFn: async () => {
      const result = (await client.callTool({
        name: "TRIGGER_LIST",
        arguments: {},
      })) as { structuredContent?: unknown };
      const payload = (result.structuredContent ?? result) as {
        triggers: RawTriggerDefinition[];
      };
      return payload.triggers.map(normalizeTrigger);
    },
    enabled: !!connectionId,
    staleTime: 30_000,
  });
}

// ============================================================================
// Types
// ============================================================================

export interface AutomationListItem {
  id: string;
  name: string;
  active: boolean;
  created_by: string;
  created_at: string;
  trigger_count: number;
  virtual_mcp_id: string;
  nearest_next_run_at: string | null;
}

export interface AutomationTrigger {
  id: string;
  type: "cron" | "event" | "webhook";
  cron_expression: string | null;
  connection_id: string | null;
  event_type: string | null;
  params: Record<string, string> | null;
  last_run_at: string | null;
  next_run_at: string | null;
  api_key_id: string | null;
  created_at: string;
}

export interface AutomationDetail {
  id: string;
  name: string;
  active: boolean;
  created_by: string;
  created_at: string;
  updated_at: string;
  virtual_mcp_id: string;
  messages: unknown[];
  models: {
    tier?: "fast" | "smart" | "thinking";
    // Specific-model override (coexists with tier; takes precedence when set).
    modelId?: string;
    credentialId?: string;
    modelTitle?: string;
    [key: string]: unknown;
  };
  // Tool allowlist (model-facing/raw tool names). null = all of the agent's
  // tools.
  tools: string[] | null;
  // Parent agent-loop step cap. null = platform default (PARENT_STEP_LIMIT).
  maxAgentSteps: number | null;
  temperature: number;
  triggers: AutomationTrigger[];
}

// ============================================================================
// Query Hooks
// ============================================================================

type AutomationListOutput = { automations: AutomationListItem[] };

export function useAutomations(virtualMcpId?: string | null) {
  const { org } = useProjectContext();
  const studio = useStudioTools();

  return useQuery({
    queryKey: KEYS.automations(org.id, virtualMcpId),
    queryFn: async () => {
      const payload = (await studio.call(
        "AUTOMATION_LIST",
        virtualMcpId !== undefined && virtualMcpId !== null
          ? { virtual_mcp_id: virtualMcpId }
          : {},
      )) as AutomationListOutput;
      return payload.automations;
    },
    staleTime: 10_000,
  });
}

type AutomationGetOutput = { automation: AutomationDetail | null };

export function useAutomation(id: string) {
  const { org } = useProjectContext();
  const studio = useStudioTools();

  return useQuery({
    queryKey: KEYS.automation(org.id, id),
    queryFn: async () => {
      const payload = (await studio.call("AUTOMATION_GET", {
        id,
      })) as AutomationGetOutput;
      return payload.automation;
    },
    enabled: !!id,
    staleTime: 10_000,
  });
}

// ============================================================================
// Run stats hook
// ============================================================================

export interface AutomationRunStats {
  runs: {
    total: number;
    completed: number;
    failed: number;
    inProgress: number;
  };
  usage: {
    calls: number;
    inputTokens: number;
    outputTokens: number;
    totalTokens: number;
    costUsd: number;
    sampledRuns: number;
    truncated: boolean;
  };
}

export function useAutomationRunStats(
  automationId: string,
  range?: { startDate?: string; endDate?: string },
) {
  const { org } = useProjectContext();
  const studio = useStudioTools();

  return useQuery({
    queryKey: KEYS.automationRunStats(
      org.id,
      automationId,
      JSON.stringify(range ?? {}),
    ),
    queryFn: async () => {
      return (await studio.call("AUTOMATION_RUN_STATS", {
        automation_id: automationId,
        ...(range?.startDate ? { startDate: range.startDate } : {}),
        ...(range?.endDate ? { endDate: range.endDate } : {}),
      })) as AutomationRunStats;
    },
    enabled: !!automationId,
    staleTime: 30_000,
  });
}

// ============================================================================
// Helpers
// ============================================================================

export function buildDefaultAutomationInput(virtualMcpId: string) {
  return {
    name: "New Automation",
    messages: [],
    models: { tier: "smart" as const },
    temperature: 0.5,
    active: true,
    virtual_mcp_id: virtualMcpId,
  };
}

// ============================================================================
// Actions Hook
// ============================================================================

export function useAutomationActions() {
  const { org } = useProjectContext();
  const studio = useStudioTools();
  const queryClient = useQueryClient();

  const invalidateAll = () =>
    queryClient.invalidateQueries({ queryKey: KEYS.automationsAll(org.id) });

  const invalidateOne = (id: string) =>
    queryClient.invalidateQueries({ queryKey: KEYS.automation(org.id, id) });

  const create = useMutation({
    mutationFn: async (input: Record<string, unknown>) => {
      return (await studio.call(
        "AUTOMATION_CREATE",
        input as ToolInput<"AUTOMATION_CREATE">,
      )) as { id: string; name: string };
    },
    onSuccess: () => {
      invalidateAll();
      toast.success("Automation created successfully");
    },
    onError: (error: unknown) => {
      const message = error instanceof Error ? error.message : String(error);
      toast.error(`Failed to create automation: ${message}`);
    },
  });

  const update = useMutation({
    mutationFn: async (input: Record<string, unknown>) => {
      return (await studio.call(
        "AUTOMATION_UPDATE",
        input as ToolInput<"AUTOMATION_UPDATE">,
      )) as { id: string };
    },
    onSuccess: (_data, variables) => {
      invalidateAll();
      if (typeof variables.id === "string") {
        invalidateOne(variables.id);
      }
      toast.success("Automation updated successfully");
    },
    onError: (error: unknown) => {
      const message = error instanceof Error ? error.message : String(error);
      toast.error(`Failed to update automation: ${message}`);
    },
  });

  const remove = useMutation({
    mutationFn: async (id: string) => {
      return (await studio.call("AUTOMATION_DELETE", { id })) as {
        success: boolean;
      };
    },
    onSuccess: (_data, id) => {
      queryClient.removeQueries({ queryKey: KEYS.automation(org.id, id) });
      invalidateAll();
      toast.success("Automation deleted successfully");
    },
    onError: (error: unknown) => {
      const message = error instanceof Error ? error.message : String(error);
      toast.error(`Failed to delete automation: ${message}`);
    },
  });

  const triggerAdd = useMutation({
    mutationFn: async (input: Record<string, unknown>) => {
      return (await studio.call(
        "AUTOMATION_TRIGGER_ADD",
        input as ToolInput<"AUTOMATION_TRIGGER_ADD">,
      )) as {
        id: string;
        automation_id: string;
        // Only set for webhook triggers. Plaintext token is shown only once.
        webhook?: { url: string; token: string } | null;
      };
    },
    onSuccess: (data) => {
      invalidateAll();
      invalidateOne(data.automation_id);
    },
  });

  const triggerRotateToken = useMutation({
    mutationFn: async (input: {
      trigger_id: string;
      automation_id: string;
    }) => {
      return (await studio.call("AUTOMATION_TRIGGER_ROTATE_TOKEN", {
        trigger_id: input.trigger_id,
      })) as {
        trigger_id: string;
        url: string;
        token: string;
      };
    },
    onSuccess: (_data, variables) => {
      invalidateAll();
      invalidateOne(variables.automation_id);
    },
  });

  const triggerRemove = useMutation({
    mutationFn: async (input: {
      trigger_id: string;
      automation_id: string;
    }) => {
      return (await studio.call("AUTOMATION_TRIGGER_REMOVE", {
        trigger_id: input.trigger_id,
      })) as { success: boolean };
    },
    onSuccess: (_data, variables) => {
      invalidateAll();
      invalidateOne(variables.automation_id);
    },
  });

  const run = useMutation({
    mutationFn: async (id: string) => {
      return (await studio.call("AUTOMATION_RUN", { id })) as {
        threadId?: string;
        error?: string;
        skipped?: string;
      };
    },
    onSuccess: (data) => {
      if (data.skipped) {
        toast.info(`Automation skipped: ${data.skipped}`);
      } else if (data.error) {
        toast.error(`Automation failed: ${data.error}`);
      } else {
        toast.success("Automation triggered");
      }
    },
    onError: (error: unknown) => {
      const message = error instanceof Error ? error.message : String(error);
      toast.error(`Failed to run automation: ${message}`);
    },
  });

  return {
    create,
    update,
    remove,
    triggerAdd,
    triggerRemove,
    triggerRotateToken,
    run,
  };
}
