/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useEffect, useRef } from "react";
import { DraggableWindow } from "./DraggableWindow.tsx";
import { FileViewer } from "./FileViewer.tsx";
import { useWindowManager } from "./WindowManager.tsx";
import {
  downloadSSHFile,
  readSSHFile,
  writeSSHFile,
  getSSHStatus,
  connectSSH,
} from "@/main-axios.ts";
import { toast } from "sonner";
import type { SSHHost } from "@/types/index";
import { useTranslation } from "react-i18next";

interface FileItem {
  name: string;
  type: "file" | "directory" | "link";
  path: string;
  size?: number;
  modified?: string;
  permissions?: string;
  owner?: string;
  group?: string;
}

interface FileWindowProps {
  windowId: string;
  file: FileItem;
  sshSessionId: string;
  sshHost: SSHHost;
  initialX?: number;
  initialY?: number;
  onFileNotFound?: (file: FileItem) => void;
}

function isDisplayableText(str: string): boolean {
  let printable = 0;
  for (let i = 0; i < Math.min(str.length, 1000); i++) {
    const code = str.charCodeAt(i);
    if (
      (code >= 32 && code <= 126) ||
      code === 9 ||
      code === 10 ||
      code === 13 ||
      code >= 128
    ) {
      printable++;
    }
  }
  return printable / Math.min(str.length, 1000) > 0.85;
}

export function FileWindow({
  windowId,
  file,
  sshSessionId,
  sshHost,
  initialX = 100,
  initialY = 100,
  onFileNotFound,
}: FileWindowProps) {
  const { closeWindow, maximizeWindow, focusWindow, windows } =
    useWindowManager();

  const { t } = useTranslation();

  const [content, setContent] = useState<string>("");
  const [isLoading, setIsLoading] = useState(false);
  const [isSaving, setIsSaving] = useState(false);
  const [isEditable, setIsEditable] = useState(false);
  const [pendingContent, setPendingContent] = useState<string>("");
  const [resetKey, setResetKey] = useState(0);
  const [mediaDimensions, setMediaDimensions] = useState<
    { width: number; height: number } | undefined
  >();
  const [externalEditorPath, setExternalEditorPath] = useState<string>("");
  const autoSaveTimerRef = useRef<NodeJS.Timeout | null>(null);
  const externalEditorRef = useRef<{
    editId: string;
    unsubscribe?: () => void;
  } | null>(null);

  const currentWindow = windows.find((w) => w.id === windowId);

  useEffect(() => {
    if (!window.electronAPI?.isElectron) return;

    window.electronAPI
      .getSetting?.("fileManager.externalEditorPath")
      .then((value) => {
        if (typeof value === "string") setExternalEditorPath(value);
      })
      .catch(() => {});
  }, []);

  const ensureSSHConnection = async () => {
    try {
      const status = await getSSHStatus(sshSessionId);

      if (!status.connected) {
        await connectSSH(sshSessionId, {
          hostId: sshHost.id,
          ip: sshHost.ip,
          port: sshHost.port,
          username: sshHost.username,
          password: sshHost.password,
          sshKey: sshHost.key,
          keyPassword: sshHost.keyPassword,
          authType: sshHost.authType,
          credentialId: sshHost.credentialId,
          userId: sshHost.userId,
        });
      }
    } catch (error) {
      console.error("SSH connection check/reconnect failed:", error);
      throw error;
    }
  };

  useEffect(() => {
    const loadFileContent = async () => {
      if (file.type !== "file") return;

      try {
        setIsLoading(true);

        await ensureSSHConnection();

        const response = await readSSHFile(sshSessionId, file.path);
        let fileContent = response.content || "";

        if (response.encoding === "base64") {
          try {
            const bytes = Uint8Array.from(atob(fileContent), (c) =>
              c.charCodeAt(0),
            );
            const decoded = new TextDecoder("utf-8").decode(bytes);
            if (isDisplayableText(decoded)) {
              fileContent = decoded;
            }
          } catch (err) {
            console.error("Failed to decode base64 content:", err);
          }
        }

        setContent(fileContent);
        setPendingContent(fileContent);
        setResetKey((k) => k + 1);

        if (!file.size) {
          const contentSize = new Blob([fileContent]).size;
          file.size = contentSize;
        }

        const mediaExtensions = [
          "jpg",
          "jpeg",
          "png",
          "gif",
          "bmp",
          "svg",
          "webp",
          "tiff",
          "ico",
          "mp3",
          "wav",
          "ogg",
          "aac",
          "flac",
          "m4a",
          "wma",
          "mp4",
          "avi",
          "mov",
          "wmv",
          "flv",
          "mkv",
          "webm",
          "m4v",
          "zip",
          "rar",
          "7z",
          "tar",
          "gz",
          "bz2",
          "xz",
          "exe",
          "dll",
          "so",
          "dylib",
          "bin",
          "iso",
        ];

        const extension = file.name.split(".").pop()?.toLowerCase();
        setIsEditable(!mediaExtensions.includes(extension || ""));
      } catch (error: unknown) {
        console.error("Failed to load file:", error);

        const err = error as {
          message?: string;
          isFileNotFound?: boolean;
          response?: {
            status?: number;
            data?: {
              tooLarge?: boolean;
              error?: string;
              fileNotFound?: boolean;
            };
          };
        };
        const errorData = err?.response?.data;
        if (errorData?.tooLarge) {
          toast.error(`File too large: ${errorData.error}`, {
            duration: 10000,
          });
        } else if (
          err.message?.includes("connection") ||
          err.message?.includes("established")
        ) {
          toast.error(
            `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`,
          );
        } else {
          const errorMessage =
            errorData?.error || err.message || "Unknown error";
          const isFileNotFound =
            err.isFileNotFound ||
            errorData?.fileNotFound ||
            err.response?.status === 404 ||
            errorMessage.includes("File not found") ||
            errorMessage.includes("No such file or directory") ||
            errorMessage.includes("cannot access") ||
            errorMessage.includes("not found") ||
            errorMessage.includes("Resource not found");

          if (isFileNotFound && onFileNotFound) {
            onFileNotFound(file);
            toast.error(
              t("fileManager.fileNotFoundAndRemoved", { name: file.name }),
            );

            closeWindow(windowId);
            return;
          } else {
            toast.error(
              t("fileManager.failedToLoadFile", {
                error: errorMessage.includes("Server error occurred")
                  ? t("fileManager.serverErrorOccurred")
                  : errorMessage,
              }),
            );
          }
        }
      } finally {
        setIsLoading(false);
      }
    };

    loadFileContent();
  }, [file, sshSessionId, sshHost]);

  const handleRevert = async () => {
    const loadFileContent = async () => {
      if (file.type !== "file") return;

      try {
        setIsLoading(true);

        await ensureSSHConnection();

        const response = await readSSHFile(sshSessionId, file.path, {
          force: true,
        });
        const fileContent = response.content || "";
        setContent(fileContent);
        setPendingContent("");
        setResetKey((k) => k + 1);

        if (!file.size) {
          const contentSize = new Blob([fileContent]).size;
          file.size = contentSize;
        }
      } catch (error: unknown) {
        console.error("Failed to load file content:", error);
        const err = error as { message?: string };
        toast.error(
          `${t("fileManager.failedToLoadFile")}: ${err.message || t("fileManager.unknownError")}`,
        );
      } finally {
        setIsLoading(false);
      }
    };

    loadFileContent();
  };

  const handleSave = async (newContent: string) => {
    try {
      setIsSaving(true);

      await ensureSSHConnection();

      await writeSSHFile(sshSessionId, file.path, newContent);
      setContent(newContent);
      setPendingContent("");

      if (autoSaveTimerRef.current) {
        clearTimeout(autoSaveTimerRef.current);
        autoSaveTimerRef.current = null;
      }

      toast.success(t("fileManager.fileSavedSuccessfully"));
    } catch (error: unknown) {
      console.error("Failed to save file:", error);

      const err = error as { message?: string };
      if (
        err.message?.includes("connection") ||
        err.message?.includes("established")
      ) {
        toast.error(
          `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`,
        );
      } else {
        toast.error(
          `${t("fileManager.failedToSaveFile")}: ${err.message || t("fileManager.unknownError")}`,
        );
      }
    } finally {
      setIsSaving(false);
    }
  };

  const closeExternalEditor = async () => {
    const session = externalEditorRef.current;
    if (!session) return;

    session.unsubscribe?.();
    externalEditorRef.current = null;

    try {
      await window.electronAPI?.closeExternalEditor?.(session.editId);
    } catch (error) {
      console.error("Failed to close external editor session:", error);
    }
  };

  const handleOpenExternalEditor = async () => {
    if (!window.electronAPI?.isElectron) {
      toast.error(t("fileManager.externalEditorDesktopOnly"));
      return;
    }

    try {
      await closeExternalEditor();
      const result = await window.electronAPI.openExternalEditor({
        fileName: file.name,
        content: pendingContent || content,
        encoding: "utf8",
        editorPath: externalEditorPath || null,
      });

      if (!result.success || !result.editId) {
        throw new Error(result.error || "Failed to open external editor");
      }

      const unsubscribe = window.electronAPI.onExternalEditorSaved?.(
        (payload) => {
          if (payload.editId !== result.editId) return;
          void handleSave(payload.content);
        },
      );

      externalEditorRef.current = {
        editId: result.editId,
        unsubscribe,
      };
      toast.success(t("fileManager.externalEditorOpened"));
    } catch (error: unknown) {
      console.error("Failed to open external editor:", error);
      const err = error as { message?: string };
      toast.error(
        `${t("fileManager.failedToOpenExternalEditor")}: ${
          err.message || t("fileManager.unknownError")
        }`,
      );
    }
  };

  const handleChooseExternalEditor = async () => {
    if (!window.electronAPI?.isElectron) {
      toast.error(t("fileManager.externalEditorDesktopOnly"));
      return;
    }

    try {
      const result = await window.electronAPI.showOpenDialog({
        title: t("fileManager.chooseExternalEditor"),
        properties: ["openFile"],
      });
      const editorPath = result.filePaths?.[0];
      if (result.canceled || !editorPath) return;

      await window.electronAPI.setSetting?.(
        "fileManager.externalEditorPath",
        editorPath,
      );
      setExternalEditorPath(editorPath);
      toast.success(t("fileManager.externalEditorSelected"));
    } catch (error: unknown) {
      console.error("Failed to select external editor:", error);
      const err = error as { message?: string };
      toast.error(
        `${t("fileManager.failedToSelectExternalEditor")}: ${
          err.message || t("fileManager.unknownError")
        }`,
      );
    }
  };

  const handleContentChange = (newContent: string) => {
    setPendingContent(newContent);

    if (autoSaveTimerRef.current) {
      clearTimeout(autoSaveTimerRef.current);
      autoSaveTimerRef.current = null;
    }

    if (newContent !== content) {
      autoSaveTimerRef.current = setTimeout(async () => {
        try {
          await handleSave(newContent);
          toast.success(t("fileManager.fileAutoSaved"));
        } catch (error) {
          console.error("Auto-save failed:", error);
          toast.error(t("fileManager.autoSaveFailed"));
        }
      }, 60000);
    }
  };

  useEffect(() => {
    return () => {
      if (autoSaveTimerRef.current) {
        clearTimeout(autoSaveTimerRef.current);
      }
      void closeExternalEditor();
    };
  }, []);

  const handleDownload = async () => {
    try {
      await ensureSSHConnection();

      const response = await downloadSSHFile(sshSessionId, file.path);

      if (response?.content) {
        const byteCharacters = atob(response.content);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
          byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray], {
          type: response.mimeType || "application/octet-stream",
        });

        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = response.fileName || file.name;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);

        toast.success(t("fileManager.fileDownloadedSuccessfully"));
      }
    } catch (error: unknown) {
      console.error("Failed to download file:", error);

      const err = error as { message?: string };
      if (
        err.message?.includes("connection") ||
        err.message?.includes("established")
      ) {
        toast.error(
          `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`,
        );
      } else {
        toast.error(
          `Failed to download file: ${err.message || "Unknown error"}`,
        );
      }
    }
  };

  const handleClose = () => {
    void closeExternalEditor();
    closeWindow(windowId);
  };

  const handleMaximize = () => {
    maximizeWindow(windowId);
  };

  const handleFocus = () => {
    focusWindow(windowId);
  };

  const handleMediaDimensionsChange = (dimensions: {
    width: number;
    height: number;
  }) => {
    setMediaDimensions(dimensions);
  };

  if (!currentWindow) {
    return null;
  }

  return (
    <DraggableWindow
      title={file.name}
      initialX={initialX}
      initialY={initialY}
      initialWidth={800}
      initialHeight={600}
      minWidth={400}
      minHeight={300}
      onClose={handleClose}
      onMaximize={handleMaximize}
      onFocus={handleFocus}
      isMaximized={currentWindow.isMaximized}
      zIndex={currentWindow.zIndex}
      targetSize={mediaDimensions}
    >
      <FileViewer
        file={file}
        content={pendingContent || content}
        savedContent={content}
        isLoading={isLoading}
        isSaving={isSaving}
        resetKey={resetKey}
        onRevert={handleRevert}
        isEditable={isEditable}
        onContentChange={handleContentChange}
        onSave={(newContent) => handleSave(newContent)}
        onDownload={handleDownload}
        onOpenExternal={
          window.electronAPI?.isElectron && isEditable
            ? handleOpenExternalEditor
            : undefined
        }
        onChooseExternalEditor={
          window.electronAPI?.isElectron && isEditable
            ? handleChooseExternalEditor
            : undefined
        }
        onMediaDimensionsChange={handleMediaDimensionsChange}
      />
    </DraggableWindow>
  );
}
