"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_BATCH_SIZE = void 0;
exports.batchRun = batchRun;
exports.listFilesRecursively = listFilesRecursively;
exports.fileSync = fileSync;
exports.downloadFile = downloadFile;
exports.uploadFile = uploadFile;
exports.uploadDirectory = uploadDirectory;
exports.downloadDirectory = downloadDirectory;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const KnowhowClient_1 = require("./services/KnowhowClient");
const login_1 = require("./login");
const config_1 = require("./config");
const services_1 = require("./services");
const hashes_1 = require("./hashes");
exports.DEFAULT_BATCH_SIZE = 5;
async function batchRun(tasks, batchSize = exports.DEFAULT_BATCH_SIZE) {
    const results = [];
    for (let i = 0; i < tasks.length; i += batchSize) {
        const batch = tasks.slice(i, i + batchSize);
        const batchResults = await Promise.all(batch.map((t) => t()));
        results.push(...batchResults);
    }
    return results;
}
function expandHome(p) {
    if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
        return path.join(os.homedir(), p.slice(1));
    }
    return p;
}
function isDirectoryPath(p) {
    return p.endsWith("/");
}
function listFilesRecursively(dir) {
    const results = [];
    if (!fs.existsSync(dir))
        return results;
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.isDirectory()) {
            const subFiles = listFilesRecursively(path.join(dir, entry.name));
            for (const f of subFiles) {
                results.push(entry.name + "/" + f);
            }
        }
        else {
            results.push(entry.name);
        }
    }
    return results;
}
async function fileSync(options = {}) {
    const { upload = false, download = false, apiUrl = KnowhowClient_1.KNOWHOW_API_URL, configPath = "./knowhow.json", dryRun = false, } = options;
    const config = await (0, config_1.getConfig)();
    if (!config.files || config.files.length === 0) {
        console.log("✓ No files configured, skipping sync");
        return;
    }
    const jwt = await (0, login_1.loadJwt)();
    if (!jwt) {
        console.error("❌ No JWT token found. Please run 'knowhow login' first.");
        process.exit(1);
    }
    const client = new KnowhowClient_1.KnowhowSimpleClient(apiUrl, jwt);
    const { AwsS3 } = (0, services_1.services)();
    console.log(`🔄 Starting file sync (${config.files.length} mounts)...`);
    let successCount = 0;
    let failCount = 0;
    for (const mount of config.files) {
        const { remotePath, localPath: rawLocalPath, direction = "download" } = mount;
        const localPath = expandHome(rawLocalPath);
        let actualDirection = direction;
        if (upload) {
            actualDirection = "upload";
        }
        else if (download) {
            actualDirection = "download";
        }
        try {
            if (actualDirection === "download") {
                if (isDirectoryPath(remotePath) || isDirectoryPath(localPath)) {
                    const count = await downloadDirectory(client, AwsS3, remotePath, localPath, dryRun);
                    successCount += count;
                }
                else {
                    await downloadFile(client, AwsS3, remotePath, localPath, dryRun);
                    successCount++;
                }
            }
            else if (actualDirection === "upload") {
                if (isDirectoryPath(remotePath) || isDirectoryPath(localPath)) {
                    const count = await uploadDirectory(client, AwsS3, remotePath, localPath, dryRun);
                    successCount += count;
                }
                else {
                    await uploadFile(client, AwsS3, remotePath, localPath, dryRun);
                    successCount++;
                }
            }
        }
        catch (error) {
            console.error(`❌ Failed to sync ${remotePath}: ${error.message}`);
            failCount++;
        }
    }
    console.log(`\n✓ Sync complete: ${successCount} succeeded, ${failCount} failed`);
    if (failCount > 0) {
        process.exit(1);
    }
}
async function downloadFile(client, s3Service, remotePath, localPath, dryRun, hashes) {
    console.log(`⬇️  Downloading ${remotePath} → ${localPath}`);
    if (dryRun) {
        console.log(`   [DRY RUN] Would download to ${localPath}`);
        return;
    }
    try {
        if (hashes && await (0, hashes_1.isLocalFileMatchingDownloadHash)(localPath, hashes)) {
            console.log(`   ✓ Skipping ${localPath} (matches stored download hash)`);
            return;
        }
        const { downloadUrl, checksumSHA256 } = await client.getOrgFilePresignedDownloadUrl(remotePath);
        if ((0, hashes_1.isLocalFileMatchingRemote)(localPath, checksumSHA256)) {
            console.log(`   ✓ Skipping ${localPath} (matches remote checksum)`);
            await (0, hashes_1.saveDownloadHash)(localPath, hashes);
            return;
        }
        const dir = path.dirname(localPath);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        await s3Service.downloadFromPresignedUrl(downloadUrl, localPath);
        await (0, hashes_1.saveDownloadHash)(localPath, hashes);
        const stats = fs.statSync(localPath);
        console.log(`   ✓ Downloaded ${stats.size} bytes`);
    }
    catch (error) {
        throw error;
    }
}
async function uploadFile(client, s3Service, remotePath, localPath, dryRun, hashes) {
    console.log(`⬆️  Uploading ${localPath} → ${remotePath}`);
    if (dryRun) {
        console.log(`   [DRY RUN] Would upload from ${localPath}`);
        return;
    }
    if (!fs.existsSync(localPath)) {
        console.warn(`   ⚠️  Local file not found: ${localPath}`);
        return;
    }
    const changed = hashes ? await (0, hashes_1.hasFileChangedSinceUpload)(localPath, hashes) : true;
    if (!changed) {
        console.log(`   ✓ Skipping ${localPath} (unchanged since last upload)`);
        return;
    }
    const presignedUrl = await client.getOrgFilePresignedUploadUrl(remotePath, localPath);
    await s3Service.uploadToPresignedUrl(presignedUrl, localPath);
    await client.markOrgFileUploadComplete(remotePath);
    await (0, hashes_1.saveUploadHash)(localPath, hashes);
    const stats = fs.statSync(localPath);
    console.log(`   ✓ Uploaded ${stats.size} bytes`);
}
async function uploadDirectory(client, s3Service, remotePath, localPath, dryRun) {
    const remoteDir = remotePath.endsWith("/") ? remotePath : remotePath + "/";
    const localDir = localPath.endsWith("/") ? localPath : localPath + "/";
    console.log(`⬆️  Uploading directory ${localDir} → ${remoteDir}`);
    const hashes = await (0, hashes_1.getHashes)();
    if (!fs.existsSync(localDir)) {
        console.warn(`   ⚠️  Local directory not found: ${localDir}`);
        return 0;
    }
    const localFiles = listFilesRecursively(localDir);
    if (localFiles.length === 0) {
        console.log(`   ⚠️  No local files found under ${localDir}`);
        return 0;
    }
    console.log(`   Found ${localFiles.length} local file(s)`);
    const tasks = localFiles.map((relFile) => async () => {
        const localFilePath = localDir + relFile;
        const remoteFilePath = remoteDir + relFile;
        try {
            await uploadFile(client, s3Service, remoteFilePath, localFilePath, dryRun, hashes);
            return 1;
        }
        catch (error) {
            console.error(`   ❌ Failed to upload ${localFilePath}, skipping: ${error.message}`);
            return 0;
        }
    });
    const counts = await batchRun(tasks);
    await (0, hashes_1.saveHashes)(hashes);
    return counts.reduce((sum, n) => sum + n, 0);
}
async function downloadDirectory(client, s3Service, remotePath, localPath, dryRun) {
    const remoteDir = remotePath.endsWith("/") ? remotePath : remotePath + "/";
    const localDir = localPath.endsWith("/") ? localPath : localPath + "/";
    console.log(`⬇️  Downloading directory ${remoteDir} → ${localDir}`);
    const hashes = await (0, hashes_1.getHashes)();
    const response = await client.listOrgFiles();
    const allFiles = response.data;
    const matchingFiles = allFiles.filter((f) => {
        const fullPath = f.folderPath.endsWith("/")
            ? f.folderPath + f.fileName
            : f.folderPath + "/" + f.fileName;
        return f.fileName !== "" && fullPath.startsWith(remoteDir);
    });
    if (matchingFiles.length === 0) {
        console.log(`   ⚠️  No remote files found under ${remoteDir}`);
        return 0;
    }
    console.log(`   Found ${matchingFiles.length} remote file(s)`);
    const tasks = matchingFiles.map((f) => async () => {
        const fullRemotePath = f.folderPath.endsWith("/")
            ? f.folderPath + f.fileName
            : f.folderPath + "/" + f.fileName;
        const relativePath = fullRemotePath.slice(remoteDir.length);
        const localFilePath = localDir + relativePath;
        await downloadFile(client, s3Service, fullRemotePath, localFilePath, dryRun, hashes);
        return 1;
    });
    const counts = await batchRun(tasks);
    await (0, hashes_1.saveHashes)(hashes);
    return counts.reduce((sum, n) => sum + n, 0);
}
//# sourceMappingURL=fileSync.js.map