import { mkdir, writeFile } from "node:fs/promises";
import * as path from "node:path";
import { getBucketeerUrlFromSuperblocksUrl } from "@superblocksteam/util";
import { getErrorMeta } from "../util/logger.js";
import { LocalContextStorageV2 } from "./llm/context-v2/storage/local.js";
import { getScopedToken } from "./util/scoped-token-utils.js";
/**
 * Type guard to check if a parsed context record is V2 format.
 */
function isContextRecordV2(record) {
    return (typeof record === "object" &&
        record !== null &&
        "version" in record &&
        record.version === 2);
}
/**
 * Downloads and restores all context files from Bucketeer checkpoint
 * Parses multipart/form-data response and saves files to local storage
 */
export async function restoreContextFromCheckpoint({ applicationId, commitId, branchName, contextId, appRootDirPath, superblocksBaseUrl, logger, jwt, contextManagerV2, }) {
    const contextStorageV2 = new LocalContextStorageV2(appRootDirPath);
    // Get scoped token for Bucketeer access
    let scopedToken;
    try {
        scopedToken = await getScopedToken({
            applicationId,
            superblocksBaseUrl,
            jwt,
        });
    }
    catch (error) {
        logger.error("[restoreContextFromCheckpoint] Failed to get scoped token", getErrorMeta(error));
        throw error;
    }
    // Download context files from Bucketeer
    const bucketeerUrl = getBucketeerUrlFromSuperblocksUrl(superblocksBaseUrl);
    const url = `${bucketeerUrl}/applications/${applicationId}/branches/${encodeURIComponent(branchName)}/commits/${encodeURIComponent(commitId)}/context`;
    const response = await fetch(url, {
        method: "GET",
        headers: {
            Authorization: `Bearer ${scopedToken}`,
        },
    });
    if (!response.ok) {
        if (response.status === 404) {
            logger.info(`[restoreContextFromCheckpoint] Context files not found for commitId=${commitId}`);
            return {
                success: false,
                savedFiles: [],
                failedFiles: [],
            };
        }
        const errorText = await response.text().catch(() => "Unable to read error");
        logger.warn(`[restoreContextFromCheckpoint] Failed to download context: ${response.status} ${response.statusText} - ${errorText}`);
        return {
            success: false,
            savedFiles: [],
            failedFiles: [],
        };
    }
    // Parse multipart/form-data response (always multipart, even for single file)
    const contentType = response.headers.get("content-type") || "";
    if (!contentType.includes("multipart/form-data")) {
        // Fallback: if not multipart, try to parse as JSON (shouldn't happen with new endpoint)
        const contextJson = await response.text();
        const contextRecord = JSON.parse(contextJson);
        // Only save if it's V2 format
        if (isContextRecordV2(contextRecord)) {
            await contextStorageV2.saveContext(contextId, contextRecord);
            logger.info(`[restoreContextFromCheckpoint] Saved V2 context for contextId=${contextId.name}`);
            contextManagerV2.clearContext(contextId);
            return {
                success: true,
                savedFiles: ["main.json"],
                failedFiles: [],
            };
        }
        else {
            logger.warn(`[restoreContextFromCheckpoint] Context record is not V2 format, skipping restore`);
            return {
                success: false,
                savedFiles: [],
                failedFiles: [],
            };
        }
    }
    // Parse multipart response
    const formData = await response.formData();
    const savedFiles = [];
    const failedFiles = [];
    let mainJsonFound = false;
    // Get all files with field name "context"
    const contextFiles = formData.getAll("context");
    for (const file of contextFiles) {
        if (file instanceof File) {
            const fileName = file.name;
            try {
                // Only main.json is treated as a conversation history ContextRecord
                if (fileName === "main.json") {
                    const fileContent = await file.text();
                    const contextRecord = JSON.parse(fileContent);
                    // Only save if it's V2 format
                    if (isContextRecordV2(contextRecord)) {
                        await contextStorageV2.saveContext(contextId, contextRecord);
                        savedFiles.push(fileName);
                        mainJsonFound = true;
                    }
                    else {
                        logger.warn(`[restoreContextFromCheckpoint] Context record is not V2 format, skipping main.json`);
                        failedFiles.push(fileName);
                    }
                }
                else {
                    // All other files (images, binary data, or auxiliary JSONs) are written as-is
                    const contextDir = path.join(appRootDirPath, ".superblocks", "context", contextId.userId);
                    await mkdir(contextDir, { recursive: true });
                    const filePath = path.join(contextDir, fileName);
                    // Read as array buffer to support both text and binary files
                    const fileContent = await file.arrayBuffer();
                    await writeFile(filePath, Buffer.from(fileContent));
                    savedFiles.push(fileName);
                }
            }
            catch (err) {
                failedFiles.push(fileName);
                logger.warn(`[restoreContextFromCheckpoint] Failed to save context file ${fileName}`, getErrorMeta(err));
            }
        }
    }
    // Clear in-memory cache if we replaced main.json, and let context manager to reload it
    if (mainJsonFound) {
        contextManagerV2.clearContext(contextId);
    }
    const success = mainJsonFound;
    if (savedFiles.length > 0) {
        logger.info(`[restoreContextFromCheckpoint] Successfully restored ${savedFiles.length} context file(s) (${savedFiles.join(", ")}) from checkpoint commitId=${commitId}`);
        if (failedFiles.length > 0) {
            logger.warn(`[restoreContextFromCheckpoint] Failed to restore ${failedFiles.length} file(s): ${failedFiles.join(", ")}`);
        }
    }
    else if (failedFiles.length > 0) {
        logger.warn(`[restoreContextFromCheckpoint] All context files failed to restore: ${failedFiles.join(", ")}`);
    }
    else {
        logger.warn(`[restoreContextFromCheckpoint] No context files found in checkpoint`);
    }
    return {
        success,
        savedFiles,
        failedFiles,
    };
}
//# sourceMappingURL=context-download.js.map