'use strict';

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

const CHROMIUM_LOCK_FILES = new Set([
    'DevToolsActivePort',
    'SingletonCookie',
    'SingletonLock',
    'SingletonSocket',
]);

const isRecord = (value) =>
    Boolean(value) && typeof value === 'object' && !Array.isArray(value);

const assertSupportedPackage = (sessionPackage) => {
    if (!sessionPackage || sessionPackage.source !== 'whatsapp_web') {
        throw new Error('wwebjs_import_source_unsupported');
    }

    const targetProvider = sessionPackage.target_provider || 'auto';
    if (targetProvider !== 'auto' && targetProvider !== 'wwebjs') {
        throw new Error('wwebjs_import_target_provider_mismatch');
    }
};

const getLocalAuthPayload = (sessionPackage) => {
    const payload = sessionPackage.payload;
    if (!isRecord(payload)) {
        throw new Error('wwebjs_import_payload_missing');
    }

    const localAuthPayload =
        payload.wwebjs_local_auth ||
        payload.local_auth ||
        payload.wwebjsLocalAuth;

    if (!isRecord(localAuthPayload) || !isRecord(localAuthPayload.files)) {
        throw new Error('wwebjs_import_payload_unsupported');
    }

    return localAuthPayload.files;
};

const assertSafeRelativePath = (relativePath) => {
    if (
        !relativePath ||
        path.isAbsolute(relativePath) ||
        relativePath.split(/[\\/]/).includes('..') ||
        relativePath.includes('\0')
    ) {
        throw new Error(`wwebjs_import_invalid_profile_file:${relativePath}`);
    }
};

const decodeFileValue = (value) => {
    if (typeof value === 'string') {
        return Buffer.from(value, 'utf8');
    }

    if (!isRecord(value)) {
        throw new Error('wwebjs_import_invalid_file_payload');
    }

    const data = value.data ?? value.content;
    const encoding = value.encoding || 'utf8';

    if (typeof data !== 'string') {
        throw new Error('wwebjs_import_invalid_file_payload');
    }

    if (encoding !== 'utf8' && encoding !== 'base64') {
        throw new Error('wwebjs_import_invalid_file_encoding');
    }

    return Buffer.from(data, encoding);
};

const pathExists = async (targetPath) =>
    Boolean(await fs.promises.stat(targetPath).catch(() => undefined));

const sessionDirNameForClientId = (clientId) => {
    if (!clientId) {
        return 'session';
    }

    if (!/^[-_\w]+$/i.test(clientId)) {
        throw new Error('wwebjs_import_invalid_client_id');
    }

    return `session-${clientId}`;
};

const resolveLocalAuthSessionPath = ({ clientId, dataPath }) =>
    path.join(
        path.resolve(dataPath || './.wwebjs_auth/'),
        sessionDirNameForClientId(clientId),
    );

const writeProfileFiles = async (profilePath, files) => {
    await fs.promises.mkdir(profilePath, { recursive: true });

    await Promise.all(
        Object.entries(files).map(async ([relativePath, value]) => {
            assertSafeRelativePath(relativePath);

            const outputPath = path.join(profilePath, relativePath);
            await fs.promises.mkdir(path.dirname(outputPath), {
                recursive: true,
            });
            await fs.promises.writeFile(outputPath, decodeFileValue(value));
        }),
    );
};

const cleanChromiumProfileLocks = async (profilePath) => {
    const entries = await fs.promises
        .readdir(profilePath, { withFileTypes: true })
        .catch(() => []);

    await Promise.all(
        entries.map(async (entry) => {
            const entryPath = path.join(profilePath, entry.name);

            if (CHROMIUM_LOCK_FILES.has(entry.name)) {
                await fs.promises.rm(entryPath, {
                    recursive: true,
                    force: true,
                });
                return;
            }

            if (entry.isDirectory()) {
                await cleanChromiumProfileLocks(entryPath);
            }
        }),
    );
};

/**
 * Restores a normalized WhatsApp Web profile package into the directory used by LocalAuth.
 *
 * Raw browser cookies/localStorage are not enough to build a Chromium profile. The package must include
 * `payload.wwebjs_local_auth.files`, where each key is a profile-relative path and each value is either
 * a UTF-8 string or `{ data, encoding: "utf8" | "base64" }`.
 */
const importWhatsAppWebSessionToLocalAuth = async ({
    sessionPackage,
    clientId,
    dataPath,
    overwrite = true,
    backupPath,
    cleanupBackupOnSuccess = false,
}) => {
    assertSupportedPackage(sessionPackage);

    const files = getLocalAuthPayload(sessionPackage);
    const fileNames = Object.keys(files);
    if (!fileNames.length) {
        throw new Error('wwebjs_import_empty_profile');
    }

    const sessionPath = resolveLocalAuthSessionPath({ clientId, dataPath });
    const tempPath = `${sessionPath}.secure-import-${crypto.randomUUID()}`;
    const resolvedBackupPath =
        backupPath ||
        `${sessionPath}.backup-${Date.now()}-${crypto
            .randomUUID()
            .slice(0, 8)}`;
    let backupCreated = false;

    await fs.promises.mkdir(path.dirname(sessionPath), { recursive: true });

    try {
        await writeProfileFiles(tempPath, files);
        await cleanChromiumProfileLocks(tempPath);

        if (await pathExists(sessionPath)) {
            if (!overwrite) {
                throw new Error('wwebjs_import_auth_folder_exists');
            }

            await fs.promises.rm(resolvedBackupPath, {
                recursive: true,
                force: true,
            });
            await fs.promises.rename(sessionPath, resolvedBackupPath);
            backupCreated = true;
        }

        await fs.promises.rename(tempPath, sessionPath);

        if (backupCreated && cleanupBackupOnSuccess) {
            await fs.promises.rm(resolvedBackupPath, {
                recursive: true,
                force: true,
            });
        }

        return {
            sessionPath,
            backupPath:
                backupCreated && !cleanupBackupOnSuccess
                    ? resolvedBackupPath
                    : undefined,
            importedFiles: fileNames.sort(),
            formatVersion: sessionPackage.format_version,
            accountHint: sessionPackage.account_hint,
        };
    } catch (error) {
        await fs.promises
            .rm(tempPath, { recursive: true, force: true })
            .catch(() => undefined);

        if (backupCreated && !(await pathExists(sessionPath))) {
            await fs.promises
                .rename(resolvedBackupPath, sessionPath)
                .catch(() => undefined);
        }

        throw error;
    }
};

module.exports = {
    cleanChromiumProfileLocks,
    importWhatsAppWebSessionToLocalAuth,
    resolveLocalAuthSessionPath,
};