import { create } from "zustand";
import { persist } from "zustand/middleware";
import { capturePageSnapshot } from "@/lib/page-history";

export interface Page {
    id: string;
    title: string;
    content: string;
    parentId: string | null;
    type: "file" | "folder";
    children?: Page[];
    createdAt?: string;
    updatedAt?: string;
    icon?: string | null;
    tags?: string[];
    order?: number;
    isLocked?: boolean;
}

export type PageSyncStatus = "saved" | "saving" | "pending" | "error";

export interface PageSyncMeta {
    status: PageSyncStatus;
    message?: string;
    lastSyncedAt?: string;
}

interface EditorState {
    pages: Page[];
    activePageId: string | null;
    isLoading: boolean;
    pageSync: Record<string, PageSyncMeta>;

    fetchPages: () => Promise<void>;
    setActivePageId: (id: string | null) => void;
    addPage: (
        parentId?: string | null,
        type?: "file" | "folder",
        initialData?: { title: string; content: string },
        order?: number
    ) => Promise<void>;
    updatePage: (id: string, data: Partial<Page>) => Promise<void>;
    retryPageSync: (id: string) => Promise<void>;
    retryPendingSyncs: () => Promise<void>;
    flushPendingSyncs: () => void;
    deletePage: (id: string) => Promise<void>;
    movePage: (id: string, parentId: string | null) => Promise<void>;
    reorderPages: (data: { id: string; order: number }[]) => Promise<void>;
}

type StoreSet = (partial: Partial<EditorState> | ((state: EditorState) => Partial<EditorState>)) => void;

type PendingContentPayload = {
    content: string;
    attempts: number;
};

const CONTENT_SYNC_DEBOUNCE_MS = 600;
const CONTENT_RETRY_BASE_MS = 1200;
const CONTENT_RETRY_MAX_MS = 30000;

const pendingContentTimers = new Map<string, ReturnType<typeof setTimeout>>();
const pendingContentPayloads = new Map<string, PendingContentPayload>();

function setPageSyncMeta(set: StoreSet, id: string, meta: PageSyncMeta): void {
    set((state) => ({
        pageSync: {
            ...state.pageSync,
            [id]: meta,
        },
    }));
}

function removePageSyncMeta(set: StoreSet, id: string): void {
    set((state) => {
        if (!state.pageSync[id]) return {};
        const next = { ...state.pageSync };
        delete next[id];
        return { pageSync: next };
    });
}

function clearPendingContentSync(id: string): void {
    const timer = pendingContentTimers.get(id);
    if (timer) {
        clearTimeout(timer);
        pendingContentTimers.delete(id);
    }
    pendingContentPayloads.delete(id);
}

function scheduleContentSync(id: string, delayMs: number, set: StoreSet): void {
    const existingTimer = pendingContentTimers.get(id);
    if (existingTimer) {
        clearTimeout(existingTimer);
    }

    const timer = setTimeout(async () => {
        pendingContentTimers.delete(id);
        const payload = pendingContentPayloads.get(id);
        if (!payload) return;

        setPageSyncMeta(set, id, { status: "saving", message: "保存中..." });

        try {
            const res = await fetch(`/api/pages/${id}`, {
                method: "PUT",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ content: payload.content }),
            });

            if (!res.ok) {
                const errorText = await res.text().catch(() => "");
                throw new Error(`Update failed: ${res.status}${errorText ? ` - ${errorText}` : ""}`);
            }

            pendingContentPayloads.delete(id);
            setPageSyncMeta(set, id, {
                status: "saved",
                message: "已保存",
                lastSyncedAt: new Date().toISOString(),
            });
        } catch (e) {
            console.error("Failed to sync page content", e);
            const latest = pendingContentPayloads.get(id);
            if (!latest) return;

            latest.attempts += 1;
            pendingContentPayloads.set(id, latest);

            const isOnline = typeof navigator === "undefined" ? true : navigator.onLine;
            setPageSyncMeta(set, id, {
                status: "pending",
                message: isOnline ? "同步失败，自动重试中" : "离线，待同步",
            });

            const retryDelay = Math.min(
                CONTENT_RETRY_MAX_MS,
                CONTENT_RETRY_BASE_MS * 2 ** Math.min(latest.attempts, 5)
            );
            scheduleContentSync(id, retryDelay, set);
        }
    }, delayMs);

    pendingContentTimers.set(id, timer);
}

export const useEditorStore = create<EditorState>()(
    persist(
        (set, get) => ({
            pages: [],
            activePageId: null,
            isLoading: false,
            pageSync: {},

            fetchPages: async () => {
                set({ isLoading: true });
                try {
                    const res = await fetch("/api/pages");
                    if (res.ok) {
                        const data = await res.json();
                        set({ pages: data });
                    }
                } catch (e) {
                    console.error("Failed to fetch pages", e);
                } finally {
                    set({ isLoading: false });
                }
            },

            setActivePageId: (id) => set({ activePageId: id }),

            addPage: async (parentId = null, type = "file", initialData, order) => {
                try {
                    const res = await fetch("/api/pages", {
                        method: "POST",
                        headers: { "Content-Type": "application/json" },
                        body: JSON.stringify({
                            title: initialData?.title || "无标题",
                            content: initialData?.content || "",
                            tags: [],
                            parentId,
                            type,
                            order,
                        }),
                    });
                    if (res.ok) {
                        const newPage = await res.json();
                        set((state) => ({
                            pages: [newPage, ...state.pages],
                            activePageId: newPage.id,
                            pageSync: {
                                ...state.pageSync,
                                [newPage.id]: {
                                    status: "saved",
                                    message: "已保存",
                                    lastSyncedAt: new Date().toISOString(),
                                },
                            },
                        }));
                    }
                } catch (e) {
                    console.error("Failed to create page", e);
                }
            },

            updatePage: async (id, data) => {
                const prevPages = get().pages;
                const current = prevPages.find((p) => p.id === id);
                const isContentOnlyUpdate = data.content !== undefined && Object.keys(data).every((key) => key === "content");

                if (current && (data.content !== undefined || data.title !== undefined)) {
                    capturePageSnapshot({
                        id: current.id,
                        title: current.title,
                        content: current.content,
                    });
                }

                set((state) => ({
                    pages: state.pages.map((p) => (p.id === id ? { ...p, ...data, updatedAt: new Date().toISOString() } : p)),
                }));

                if (isContentOnlyUpdate) {
                    pendingContentPayloads.set(id, {
                        content: data.content ?? "",
                        attempts: pendingContentPayloads.get(id)?.attempts ?? 0,
                    });
                    setPageSyncMeta(set, id, { status: "pending", message: "本地已保存，待同步" });
                    scheduleContentSync(id, CONTENT_SYNC_DEBOUNCE_MS, set);
                    return;
                }

                const pendingContent = pendingContentPayloads.get(id);
                clearPendingContentSync(id);

                const payloadToSend: Partial<Page> =
                    pendingContent && data.content === undefined
                        ? { ...data, content: pendingContent.content }
                        : data;

                setPageSyncMeta(set, id, { status: "saving", message: "保存中..." });

                try {
                    const res = await fetch(`/api/pages/${id}`, {
                        method: "PUT",
                        headers: { "Content-Type": "application/json" },
                        body: JSON.stringify(payloadToSend),
                    });
                    if (!res.ok) {
                        const errorText = await res.text().catch(() => "");
                        throw new Error(`Update failed: ${res.status}${errorText ? ` - ${errorText}` : ""}`);
                    }

                    setPageSyncMeta(set, id, {
                        status: "saved",
                        message: "已保存",
                        lastSyncedAt: new Date().toISOString(),
                    });
                } catch (e) {
                    console.error("Failed to update page", e);
                    set({ pages: prevPages });
                    setPageSyncMeta(set, id, {
                        status: "error",
                        message: "保存失败",
                    });
                }
            },

            retryPageSync: async (id) => {
                const pending = pendingContentPayloads.get(id);
                if (!pending) {
                    const page = get().pages.find((p) => p.id === id);
                    if (!page) return;
                    pendingContentPayloads.set(id, { content: page.content || "", attempts: 0 });
                } else {
                    pending.attempts = 0;
                    pendingContentPayloads.set(id, pending);
                }

                setPageSyncMeta(set, id, { status: "pending", message: "正在尝试同步..." });
                scheduleContentSync(id, 80, set);
            },

            retryPendingSyncs: async () => {
                const ids = Array.from(pendingContentPayloads.keys());
                await Promise.all(ids.map((id) => get().retryPageSync(id)));
            },

            flushPendingSyncs: () => {
                const entries = Array.from(pendingContentPayloads.entries());
                for (const [id, payload] of entries) {
                    const timer = pendingContentTimers.get(id);
                    if (timer) {
                        clearTimeout(timer);
                        pendingContentTimers.delete(id);
                    }

                    // Best-effort write on page hide/unload.
                    fetch(`/api/pages/${id}`, {
                        method: "PUT",
                        headers: { "Content-Type": "application/json" },
                        body: JSON.stringify({ content: payload.content }),
                        keepalive: true,
                    }).catch((e) => {
                        console.error("Failed to flush page sync", e);
                    });
                }
            },

            movePage: async (id, parentId) => {
                await get().updatePage(id, { parentId });
            },

            deletePage: async (id) => {
                const prevPages = get().pages;
                const prevActive = get().activePageId;

                clearPendingContentSync(id);
                removePageSyncMeta(set, id);

                set((state) => ({
                    pages: state.pages.filter((p) => p.id !== id),
                    activePageId: prevActive === id ? null : prevActive,
                }));

                try {
                    const res = await fetch(`/api/pages/${id}`, {
                        method: "DELETE",
                    });
                    if (!res.ok) {
                        throw new Error(`Delete failed: ${res.status}`);
                    }
                } catch (e) {
                    console.error("Failed to delete page", e);
                    set({ pages: prevPages, activePageId: prevActive });
                }
            },

            reorderPages: async (updates) => {
                const prevPages = get().pages;

                set((state) => {
                    const newPages = [...state.pages];
                    updates.forEach(({ id, order }) => {
                        const page = newPages.find((p) => p.id === id);
                        if (page) page.order = order;
                    });
                    return { pages: newPages };
                });

                try {
                    const res = await fetch("/api/pages/reorder", {
                        method: "PUT",
                        headers: { "Content-Type": "application/json" },
                        body: JSON.stringify({ updates }),
                    });
                    if (!res.ok) {
                        throw new Error(`Reorder failed: ${res.status}`);
                    }
                } catch (e) {
                    console.error("Failed to reorder pages", e);
                    set({ pages: prevPages });
                }
            },
        }),
        {
            name: "editor-storage",
            partialize: (state) => ({ activePageId: state.activePageId }),
        }
    )
);
