import { useCallback, useRef, useState } from "react";
import type { ToolInput } from "../../core/io/input-types";
import type { ToolResult } from "../../core/io/output-types";
import type { PlimiPlugin, ToolContext, ToolProgress } from "../../core/plugins/plugin-types";
import { runPlugin } from "../../core/plugins/plugin-runner";

function errorMessage(error: unknown): string {
  if (error instanceof DOMException && error.name === "AbortError") {
    return "Operation was cancelled.";
  }

  if (error instanceof Error) {
    return error.message || "An error occurred during execution.";
  }

  return "An error occurred during execution.";
}

export function useToolExecution<TOptions>(plugin: PlimiPlugin<TOptions>) {
  const [result, setResult] = useState<ToolResult | null>(null);
  const [isExecuting, setIsExecuting] = useState(false);
  const [progress, setProgress] = useState<ToolProgress>({
    percentage: 0,
    message: "",
  });
  const [error, setError] = useState<string | null>(null);

  const abortControllerRef = useRef<AbortController | null>(null);

  const run = useCallback(async (input: ToolInput, options: TOptions) => {
    setIsExecuting(true);
    setResult(null);
    setError(null);
    setProgress({ percentage: 0, message: "Initializing..." });

    const abortController = new AbortController();
    abortControllerRef.current = abortController;

    const context: ToolContext = {
      signal: abortController.signal,
      reportProgress: (nextProgress) => {
        setProgress({
          percentage: nextProgress.percentage ?? 0,
          message: nextProgress.message ?? "",
        });
      },
      logger: console,
    };

    try {
      const nextResult = await runPlugin(plugin, input, options, context);
      setResult(nextResult);
      return nextResult;
    } catch (err) {
      setError(errorMessage(err));
      return null;
    } finally {
      setIsExecuting(false);
      abortControllerRef.current = null;
    }
  }, [plugin]);

  const cancel = useCallback(() => {
    abortControllerRef.current?.abort();
  }, []);

  const reset = useCallback(() => {
    setResult(null);
    setError(null);
    setProgress({ percentage: 0, message: "" });
  }, []);

  return {
    run,
    cancel,
    reset,
    result,
    isExecuting,
    progress,
    error,
  };
}
