import { invoke } from "@tauri-apps/api/core";
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { extensionRegistry } from "@/extensions/registry/extension-registry";
import { useFileSystemStore } from "@/features/file-system/controllers/store";
import { gitDiffCache } from "@/features/git/utils/git-diff-cache";
import { isEditorContent } from "@/features/panes/types/pane-content";
import { createSelectors } from "@/utils/zustand-selectors";
import { writeFile } from "@/features/file-system/controllers/platform";

const HISTORY_DEBOUNCE_MS = 500;
const historyDebounceTimers = new Map<string, NodeJS.Timeout>();
const lastBufferContent = new Map<string, string>();

export function cleanupBufferHistoryTracking(bufferId: string): void {
  const timer = historyDebounceTimers.get(bufferId);
  if (timer) {
    clearTimeout(timer);
    historyDebounceTimers.delete(bufferId);
  }
  lastBufferContent.delete(bufferId);
}

interface AppState {
  autoSaveTimeoutId: NodeJS.Timeout | null;
  quickEditState: {
    isOpen: boolean;
    selectedText: string;
    cursorPosition: { x: number; y: number };
    selectionRange: { start: number; end: number };
  };
  actions: AppActions;
}

interface AppActions {
  handleContentChange: (content: string) => Promise<void>;
  handleSave: () => Promise<void>;
  openQuickEdit: (params: {
    text: string;
    cursorPosition: { x: number; y: number };
    selectionRange: { start: number; end: number };
  }) => void;
  cleanup: () => void;
}

export const useEditorAppStore = createSelectors(
  create<AppState>()(
    immer((set, get) => ({
      autoSaveTimeoutId: null,
      quickEditState: {
        isOpen: false,
        selectedText: "",
        cursorPosition: { x: 0, y: 0 },
        selectionRange: { start: 0, end: 0 },
      },
      actions: {
        handleContentChange: async (content: string) => {
          const { useBufferStore } = await import("@/features/editor/stores/buffer-store");
          const { useFileWatcherStore } =
            await import("@/features/file-system/controllers/file-watcher-store");
          const { useSettingsStore } = await import("@/features/settings/store");
          const { useHistoryStore } = await import("@/features/editor/stores/history-store");

          const { activeBufferId, buffers } = useBufferStore.getState();
          const { updateBufferContent, markBufferDirty } = useBufferStore.getState().actions;
          const { settings } = useSettingsStore.getState();
          const { markPendingSave } = useFileWatcherStore.getState();

          const activeBuffer = buffers.find((b) => b.id === activeBufferId);
          if (!activeBuffer || !isEditorContent(activeBuffer)) return;

          if (activeBufferId) {
            const lastContent = lastBufferContent.get(activeBufferId);

            if (lastContent === undefined) {
              lastBufferContent.set(activeBufferId, activeBuffer.content);
            }

            if (content !== lastContent) {
              const existingTimer = historyDebounceTimers.get(activeBufferId);
              if (existingTimer) {
                clearTimeout(existingTimer);
              }

              const timer = setTimeout(() => {
                const { pushHistory } = useHistoryStore.getState().actions;
                const oldContent = lastBufferContent.get(activeBufferId);

                if (oldContent !== undefined) {
                  pushHistory(activeBufferId, {
                    content: oldContent,
                    timestamp: Date.now(),
                  });
                }

                lastBufferContent.set(activeBufferId, content);
                historyDebounceTimers.delete(activeBufferId);
              }, HISTORY_DEBOUNCE_MS);

              historyDebounceTimers.set(activeBufferId, timer);
            }
          }

          const isRemoteFile = activeBuffer.path.startsWith("remote://");

          if (isRemoteFile) {
            updateBufferContent(activeBuffer.id, content, false);
          } else {
            updateBufferContent(activeBuffer.id, content, true);

            if (!activeBuffer.isVirtual && settings.autoSave) {
              const { autoSaveTimeoutId } = get();
              if (autoSaveTimeoutId) {
                clearTimeout(autoSaveTimeoutId);
              }

              const newTimeoutId = setTimeout(async () => {
                try {
                  markPendingSave(activeBuffer.path);
                  await writeFile(activeBuffer.path, content);
                  markBufferDirty(activeBuffer.id, false);

                  const rootFolderPath = useFileSystemStore.getState().rootFolderPath;
                  if (rootFolderPath) {
                    gitDiffCache.invalidate(rootFolderPath, activeBuffer.path);
                    setTimeout(() => {
                      window.dispatchEvent(
                        new CustomEvent("git-status-updated", {
                          detail: { filePath: activeBuffer.path },
                        }),
                      );
                    }, 50);
                  }
                } catch (error) {
                  console.error("Error saving file:", error);
                  markBufferDirty(activeBuffer.id, true);
                }
              }, 150);

              set((state) => {
                state.autoSaveTimeoutId = newTimeoutId;
              });
            }
          }
        },

        handleSave: async () => {
          const { useBufferStore } = await import("@/features/editor/stores/buffer-store");
          const { useSettingsStore } = await import("@/features/settings/store");
          const { useFileWatcherStore } =
            await import("@/features/file-system/controllers/file-watcher-store");

          const { activeBufferId, buffers } = useBufferStore.getState();
          const { markBufferDirty } = useBufferStore.getState().actions;
          const { updateSettingsFromJSON } = useSettingsStore.getState();
          const { markPendingSave } = useFileWatcherStore.getState();

          const activeBuffer = buffers.find((b) => b.id === activeBufferId);
          if (!activeBuffer || !isEditorContent(activeBuffer)) return;

          if (activeBuffer.path.startsWith("untitled:")) {
            const { save: saveDialog } = await import("@tauri-apps/plugin-dialog");
            const result = await saveDialog({
              title: "Save",
              defaultPath: activeBuffer.name,
              filters: [{ name: "All Files", extensions: ["*"] }],
            });
            if (result) {
              await writeFile(result, activeBuffer.content);
              useBufferStore.getState().actions.updateBufferPath(activeBuffer.id, result);
              markBufferDirty(activeBuffer.id, false);
            }
            return;
          }

          if (activeBuffer.isVirtual) {
            if (activeBuffer.path === "settings://user-settings.json") {
              const success = updateSettingsFromJSON(activeBuffer.content);
              markBufferDirty(activeBuffer.id, !success);
            } else {
              markBufferDirty(activeBuffer.id, false);
            }
          } else if (activeBuffer.path.startsWith("remote://")) {
            markBufferDirty(activeBuffer.id, true);
            const pathParts = activeBuffer.path.replace("remote://", "").split("/");
            const connectionId = pathParts.shift();
            const remotePath = `/${pathParts.join("/")}`;

            if (connectionId) {
              try {
                await invoke("ssh_write_file", {
                  connectionId,
                  filePath: remotePath,
                  content: activeBuffer.content,
                });
                markBufferDirty(activeBuffer.id, false);
              } catch (error) {
                console.error("Error saving remote file:", error);
                markBufferDirty(activeBuffer.id, true);
              }
            }
          } else {
            try {
              markPendingSave(activeBuffer.path);

              let contentToSave = activeBuffer.content;
              const { settings } = useSettingsStore.getState();

              if (settings.formatOnSave) {
                const { formatContent } =
                  await import("@/features/editor/formatter/formatter-service");
                const languageId = extensionRegistry.getLanguageId(activeBuffer.path);

                const formatResult = await formatContent({
                  filePath: activeBuffer.path,
                  content: activeBuffer.content,
                  languageId: languageId || undefined,
                });

                if (formatResult.success && formatResult.formattedContent) {
                  contentToSave = formatResult.formattedContent;
                  const { updateBufferContent } = useBufferStore.getState().actions;
                  updateBufferContent(activeBufferId!, contentToSave, false);
                }
              }

              await writeFile(activeBuffer.path, contentToSave);
              markBufferDirty(activeBuffer.id, false);

              if (settings.lintOnSave) {
                const { lintContent } = await import("@/features/editor/linter/linter-service");
                const { convertLintDiagnostic, useDiagnosticsStore } =
                  await import("@/features/diagnostics/stores/diagnostics-store");
                const languageId = extensionRegistry.getLanguageId(activeBuffer.path);

                const lintResult = await lintContent({
                  filePath: activeBuffer.path,
                  content: contentToSave,
                  languageId: languageId || undefined,
                });

                if (lintResult.success && lintResult.diagnostics) {
                  useDiagnosticsStore.getState().actions.setDiagnostics(
                    activeBuffer.path,
                    lintResult.diagnostics.map((diagnostic) =>
                      convertLintDiagnostic(activeBuffer.path, diagnostic),
                    ),
                  );
                }
              }

              const rootFolderPath = useFileSystemStore.getState().rootFolderPath;
              if (rootFolderPath) {
                gitDiffCache.invalidate(rootFolderPath, activeBuffer.path);
                setTimeout(() => {
                  window.dispatchEvent(
                    new CustomEvent("git-status-updated", {
                      detail: { filePath: activeBuffer.path },
                    }),
                  );
                }, 50);
              }
            } catch (error) {
              console.error("Error saving local file:", error);
              markBufferDirty(activeBuffer.id, true);
            }
          }
        },

        openQuickEdit: (params) => {
          set((state) => {
            state.quickEditState = {
              isOpen: true,
              selectedText: params.text,
              cursorPosition: params.cursorPosition,
              selectionRange: params.selectionRange,
            };
          });
        },

        cleanup: () => {
          const { autoSaveTimeoutId } = get();
          if (autoSaveTimeoutId) {
            clearTimeout(autoSaveTimeoutId);
          }
        },
      },
    })),
  ),
);
