import { useEffect, useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";

interface ModelInfo {
  id: string;
  name: string;
  filename: string;
  url?: string;
  size_mb: number;
  is_downloaded: boolean;
  is_bundled: boolean;
}

interface DownloadProgress {
  model_id: string;
  downloaded: number;
  total: number;
  percentage: number;
}

export const useModels = () => {
  const [models, setModels] = useState<ModelInfo[]>([]);
  const [currentModel, setCurrentModel] = useState<string>("");
  const [downloadingModels, setDownloadingModels] = useState<Set<string>>(
    new Set(),
  );
  const [downloadProgress, setDownloadProgress] = useState<
    Map<string, DownloadProgress>
  >(new Map());
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [hasAnyModels, setHasAnyModels] = useState(false);
  const [isFirstRun, setIsFirstRun] = useState(false);

  const loadModels = async () => {
    try {
      const modelList = await invoke<ModelInfo[]>("get_available_models");
      setModels(modelList);
      setError(null);
    } catch (err) {
      setError(`Failed to load models: ${err}`);
    } finally {
      setLoading(false);
    }
  };

  const loadCurrentModel = async () => {
    try {
      const current = await invoke<string>("get_current_model");
      setCurrentModel(current);
    } catch (err) {
      console.error("Failed to load current model:", err);
    }
  };

  const checkFirstRun = async () => {
    try {
      const hasModels = await invoke<boolean>("has_any_models_available");
      setHasAnyModels(hasModels);
      setIsFirstRun(!hasModels);
      return !hasModels;
    } catch (err) {
      console.error("Failed to check model availability:", err);
      return false;
    }
  };

  const selectModel = async (modelId: string) => {
    try {
      setError(null);
      await invoke("set_active_model", { modelId });
      setCurrentModel(modelId);
      setIsFirstRun(false);
      setHasAnyModels(true);
      return true;
    } catch (err) {
      setError(`Failed to switch to model: ${err}`);
      return false;
    }
  };

  const downloadModel = async (modelId: string) => {
    try {
      setError(null);
      setDownloadingModels((prev) => new Set(prev.add(modelId)));
      await invoke("download_model", { modelId });
      return true;
    } catch (err) {
      setError(`Failed to download model: ${err}`);
      setDownloadingModels((prev) => {
        const next = new Set(prev);
        next.delete(modelId);
        return next;
      });
      return false;
    }
  };

  const deleteModel = async (modelId: string) => {
    try {
      setError(null);
      await invoke("delete_model", { modelId });
      await loadModels(); // Refresh the list
      return true;
    } catch (err) {
      setError(`Failed to delete model: ${err}`);
      return false;
    }
  };

  const getModelInfo = (modelId: string): ModelInfo | undefined => {
    return models.find((model) => model.id === modelId);
  };

  const isModelDownloading = (modelId: string): boolean => {
    return downloadingModels.has(modelId);
  };

  const getDownloadProgress = (
    modelId: string,
  ): DownloadProgress | undefined => {
    return downloadProgress.get(modelId);
  };

  useEffect(() => {
    loadModels();
    loadCurrentModel();
    checkFirstRun();

    // Listen for download progress
    const progressUnlisten = listen<DownloadProgress>(
      "model-download-progress",
      (event) => {
        setDownloadProgress(
          (prev) => new Map(prev.set(event.payload.model_id, event.payload)),
        );
      },
    );

    // Listen for download completion
    const completeUnlisten = listen<string>(
      "model-download-complete",
      (event) => {
        const modelId = event.payload;
        setDownloadingModels((prev) => {
          const next = new Set(prev);
          next.delete(modelId);
          return next;
        });
        setDownloadProgress((prev) => {
          const next = new Map(prev);
          next.delete(modelId);
          return next;
        });
        // Refresh models list to update download status
        loadModels();
      },
    );

    return () => {
      progressUnlisten.then((fn) => fn());
      completeUnlisten.then((fn) => fn());
    };
  }, []);

  return {
    models,
    currentModel,
    loading,
    error,
    downloadingModels,
    downloadProgress,
    hasAnyModels,
    isFirstRun,
    loadModels,
    loadCurrentModel,
    checkFirstRun,
    selectModel,
    downloadModel,
    deleteModel,
    getModelInfo,
    isModelDownloading,
    getDownloadProgress,
  };
};
