import { NextRequest, NextResponse } from "next/server";
import JSZip from "jszip";
import { prisma } from "@/lib/prisma";
import { requireApiAuth } from "@/lib/api-auth";
import { markdownToImportHtml } from "@/lib/markdown-import";
import { pageToMarkdown } from "@/lib/export";
import { sanitizeFilename } from "@/lib/page-utils";
import { writeFile, mkdir } from "fs/promises";
import { join } from "path";

type ZipEntry = {
    path: string;
    isDir: boolean;
    content?: string;
};

type ParsedMarkdown = {
    title: string;
    order: number;
    tags: string[];
    htmlContent: string;
};

const MAX_RESTORE_FILE_SIZE = 50 * 1024 * 1024; // 50 MB

function normalizeZipPath(input: string): string {
    const noBackslashes = input.replace(/\\/g, "/");
    const noTrailingSlash = noBackslashes.endsWith("/") ? noBackslashes.slice(0, -1) : noBackslashes;
    return noTrailingSlash.trim();
}

function getDepth(path: string): number {
    return path.split("/").length;
}

function getParentPath(path: string): string | null {
    const parts = path.split("/");
    if (parts.length <= 1) return null;
    return parts.slice(0, -1).join("/");
}

function getNameFromPath(path: string): string {
    return path.split("/").pop() || "Untitled";
}

async function parseMarkdownWithFrontmatter(filePath: string, content: string): Promise<ParsedMarkdown> {
    const defaultTitle = getNameFromPath(filePath).replace(/\.md$/i, "") || "Untitled";
    let title = defaultTitle;
    let tags: string[] = [];
    let order = 0;
    let markdownBody = content;

    const fmMatch = markdownBody.match(/^---\n([\s\S]*?)\n---\n/);
    if (fmMatch) {
        const fmString = fmMatch[1];
        markdownBody = markdownBody.slice(fmMatch[0].length);

        const titleMatch = fmString.match(/title:\s*"(.*)"/);
        if (titleMatch && titleMatch[1]) title = titleMatch[1];

        const tagsMatch = fmString.match(/tags:\s*\[(.*)\]/);
        if (tagsMatch && tagsMatch[1]) {
            tags = tagsMatch[1]
                .split(",")
                .map((item) => item.trim().replace(/^"|"$/g, ""))
                .filter(Boolean);
        }

        const orderMatch = fmString.match(/order:\s*(\d+)/);
        if (orderMatch && orderMatch[1]) {
            order = parseInt(orderMatch[1], 10);
        }
    }

    const htmlContent = markdownToImportHtml(markdownBody);
    return { title, order, tags, htmlContent };
}

function collectFolderPaths(entries: ZipEntry[]): string[] {
    const folderSet = new Set<string>();

    for (const entry of entries) {
        const normalized = normalizeZipPath(entry.path);
        if (!normalized) continue;

        if (entry.isDir) {
            folderSet.add(normalized);
        }

        let current = getParentPath(normalized);
        while (current) {
            folderSet.add(current);
            current = getParentPath(current);
        }
    }

    return Array.from(folderSet).sort((a, b) => getDepth(a) - getDepth(b));
}

/**
 * 创建当前数据库的备份 ZIP
 */
async function createBackupZip(): Promise<Buffer> {
    const allPages = await prisma.page.findMany({
        orderBy: [{ parentId: "asc" }, { order: "asc" }],
    });

    const zip = new JSZip();

    // 递归添加页面到 ZIP
    function addPagesToZip(parentId: string | null, currentFolder: JSZip) {
        const children = allPages.filter((p) => p.parentId === parentId);

        for (const page of children) {
            const cleanTitle = sanitizeFilename(page.title);

            if (page.type === "folder") {
                const subFolder = currentFolder.folder(cleanTitle);
                if (subFolder) {
                    addPagesToZip(page.id, subFolder);
                }
            } else {
                const markdown = pageToMarkdown({
                    id: page.id,
                    title: page.title,
                    content: page.content,
                    parentId: page.parentId,
                    type: page.type as "file" | "folder",
                    tags: JSON.parse(page.tags),
                    icon: page.icon,
                    order: page.order,
                    isLocked: page.isLocked,
                    createdAt: page.createdAt.toISOString(),
                    updatedAt: page.updatedAt.toISOString(),
                });
                currentFolder.file(`${cleanTitle}.md`, markdown);
            }
        }
    }

    addPagesToZip(null, zip);

    return await zip.generateAsync({ type: "nodebuffer" });
}

export async function POST(req: NextRequest) {
    const authError = await requireApiAuth();
    if (authError) return authError;

    try {
        const formData = await req.formData();
        const file = formData.get("file");

        if (!(file instanceof File)) {
            return NextResponse.json({ error: "No file uploaded" }, { status: 400 });
        }
        if (file.size <= 0) {
            return NextResponse.json({ error: "Empty file" }, { status: 400 });
        }
        if (file.size > MAX_RESTORE_FILE_SIZE) {
            return NextResponse.json({ error: "File too large" }, { status: 413 });
        }

        const buffer = await file.arrayBuffer();
        const zip = await JSZip.loadAsync(buffer);
        const entries: ZipEntry[] = [];
        const loadTasks: Promise<void>[] = [];

        zip.forEach((rawPath, zipEntry) => {
            if (rawPath.startsWith("__MACOSX") || rawPath.includes(".DS_Store")) return;

            loadTasks.push((async () => {
                const path = normalizeZipPath(rawPath);
                if (!path) return;

                if (zipEntry.dir) {
                    entries.push({ path, isDir: true });
                    return;
                }
                if (!path.endsWith(".md")) return;

                const content = await zipEntry.async("string");
                entries.push({ path, isDir: false, content });
            })());
        });

        await Promise.all(loadTasks);
        if (entries.length === 0) {
            return NextResponse.json({ error: "No valid markdown entries found in zip" }, { status: 400 });
        }

        const folderPaths = collectFolderPaths(entries);
        const fileEntries = entries
            .filter((entry): entry is ZipEntry & { content: string } => !entry.isDir && typeof entry.content === "string")
            .sort((a, b) => getDepth(a.path) - getDepth(b.path));

        // Parse markdown before DB transaction to keep lock time low.
        const parsedFiles = await Promise.all(
            fileEntries.map(async (entry) => {
                const parsed = await parseMarkdownWithFrontmatter(entry.path, entry.content);
                return { entry, parsed };
            })
        );

        // 在删除数据前创建自动备份
        let backupBuffer: Buffer | null = null;
        let backupPath: string | null = null;
        try {
            backupBuffer = await createBackupZip();

            // 保存备份到 backups 目录
            const backupsDir = join(process.cwd(), "backups");
            await mkdir(backupsDir, { recursive: true });

            const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
            backupPath = join(backupsDir, `auto-backup-before-restore-${timestamp}.zip`);
            await writeFile(backupPath, backupBuffer);

            console.log(`Auto backup created: ${backupPath}`);
        } catch (backupError) {
            console.error("Failed to create backup before restore:", backupError);
            return NextResponse.json(
                { error: "Failed to create backup before restore. Restore aborted for safety." },
                { status: 500 }
            );
        }

        await prisma.$transaction(async (tx) => {
            // 删除所有现有页面（已在上面创建备份）
            await tx.page.deleteMany();

            const folderIdMap = new Map<string, string>();
            for (const folderPath of folderPaths) {
                const parentPath = getParentPath(folderPath);
                const parentId = parentPath ? folderIdMap.get(parentPath) || null : null;

                const folder = await tx.page.create({
                    data: {
                        title: getNameFromPath(folderPath),
                        type: "folder",
                        parentId,
                    },
                });
                folderIdMap.set(folderPath, folder.id);
            }

            for (const { entry, parsed } of parsedFiles) {
                const parentPath = getParentPath(entry.path);
                const parentId = parentPath ? folderIdMap.get(parentPath) || null : null;

                await tx.page.create({
                    data: {
                        title: parsed.title,
                        type: "file",
                        content: parsed.htmlContent,
                        tags: JSON.stringify(parsed.tags),
                        order: parsed.order,
                        parentId,
                    },
                });
            }
        });

        return NextResponse.json({
            success: true,
            count: entries.length,
            files: parsedFiles.length,
            folders: folderPaths.length,
            backupPath: backupPath ? backupPath.replace(process.cwd(), "") : null,
        });
    } catch (error) {
        console.error("Restore failed:", error);
        return NextResponse.json({ error: "Restore failed" }, { status: 500 });
    }
}
