"use strict";
/**
 * GitHub Cloner Utility
 *
 * Handles cloning GitHub repositories for admin starter templates
 * and other project templates.
 */
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;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitHubCloner = void 0;
const child_process_1 = require("child_process");
const util_1 = require("util");
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class GitHubCloner {
    /**
     * Clone a GitHub repository
     */
    static async clone(options) {
        try {
            const { url, outputPath, branch = "main", depth = 1, removeGit = true, verbose = false, } = options;
            if (verbose) {
                console.log(chalk_1.default.gray(`  📥 Cloning ${url} to ${outputPath}`));
            }
            // Ensure output directory exists
            await fs.mkdir(path.dirname(outputPath), { recursive: true });
            // Clone repository
            const cloneCommand = `git clone --depth ${depth} --branch ${branch} ${url} ${outputPath}`;
            await execAsync(cloneCommand);
            // Get commit hash
            const commitHash = await this.getCommitHash(outputPath);
            // Remove .git directory if requested
            if (removeGit) {
                await fs.rm(path.join(outputPath, ".git"), {
                    recursive: true,
                    force: true,
                });
            }
            // Extract repository name from URL
            const repository = this.extractRepositoryName(url);
            const result = {
                success: true,
                outputPath,
                metadata: {
                    repository,
                    branch,
                    commitHash,
                    clonedAt: new Date().toISOString(),
                },
            };
            if (verbose) {
                console.log(chalk_1.default.green(`    ✓ Repository cloned successfully`));
                console.log(chalk_1.default.gray(`    Repository: ${repository}`));
                console.log(chalk_1.default.gray(`    Branch: ${branch}`));
                console.log(chalk_1.default.gray(`    Commit: ${commitHash.substring(0, 8)}`));
            }
            return result;
        }
        catch (error) {
            return {
                success: false,
                outputPath: options.outputPath,
                error: error instanceof Error ? error.message : String(error),
            };
        }
    }
    /**
     * Clone admin starter repository
     */
    static async cloneAdminStarter(outputPath, options = {}) {
        const defaultOptions = {
            url: "https://github.com/mycontext/admin-starter",
            outputPath,
            branch: "main",
            depth: 1,
            removeGit: true,
            verbose: true,
        };
        return this.clone({ ...defaultOptions, ...options });
    }
    /**
     * Clone a specific template repository
     */
    static async cloneTemplate(templateName, outputPath, options = {}) {
        const templateUrl = `https://github.com/mycontext/${templateName}-template`;
        const defaultOptions = {
            url: templateUrl,
            outputPath,
            branch: "main",
            depth: 1,
            removeGit: true,
            verbose: true,
        };
        return this.clone({ ...defaultOptions, ...options });
    }
    /**
     * Check if a repository exists and is accessible
     */
    static async checkRepository(url) {
        try {
            const command = `git ls-remote --heads ${url}`;
            await execAsync(command);
            return { exists: true };
        }
        catch (error) {
            return {
                exists: false,
                error: error instanceof Error ? error.message : String(error),
            };
        }
    }
    /**
     * Get available branches for a repository
     */
    static async getBranches(url) {
        try {
            const command = `git ls-remote --heads ${url}`;
            const { stdout } = await execAsync(command);
            const branches = stdout
                .split("\n")
                .filter((line) => line.trim())
                .map((line) => line.split("refs/heads/")[1])
                .filter((branch) => Boolean(branch));
            return { branches };
        }
        catch (error) {
            return {
                branches: [],
                error: error instanceof Error ? error.message : String(error),
            };
        }
    }
    /**
     * Get the latest commit hash for a repository
     */
    static async getLatestCommit(url, branch = "main") {
        try {
            const command = `git ls-remote ${url} refs/heads/${branch}`;
            const { stdout } = await execAsync(command);
            const commitHash = stdout.split("\t")[0] || "";
            return { commitHash };
        }
        catch (error) {
            return {
                commitHash: "",
                error: error instanceof Error ? error.message : String(error),
            };
        }
    }
    /**
     * Get commit hash for a cloned repository
     */
    static async getCommitHash(repoPath) {
        try {
            const command = "git rev-parse HEAD";
            const { stdout } = await execAsync(command, { cwd: repoPath });
            return stdout.trim();
        }
        catch {
            return "unknown";
        }
    }
    /**
     * Extract repository name from URL
     */
    static extractRepositoryName(url) {
        const match = url.match(/github\.com\/([^\/]+)\/([^\/]+)(?:\.git)?$/);
        if (match) {
            return `${match[1]}/${match[2]}`;
        }
        return url;
    }
    /**
     * Clone with retry logic
     */
    static async cloneWithRetry(options, maxRetries = 3) {
        let lastError;
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
            if (options.verbose && attempt > 1) {
                console.log(chalk_1.default.yellow(`  🔄 Retry attempt ${attempt}/${maxRetries}`));
            }
            const result = await this.clone(options);
            if (result.success) {
                return result;
            }
            lastError = result.error;
            if (attempt < maxRetries) {
                // Wait before retry
                await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
            }
        }
        return {
            success: false,
            outputPath: options.outputPath,
            error: `Failed after ${maxRetries} attempts. Last error: ${lastError}`,
        };
    }
    /**
     * Clone and setup project
     */
    static async cloneAndSetup(options) {
        const { installDependencies = true, runSetup = true, setupCommands = [], ...cloneOptions } = options;
        // Clone repository
        const cloneResult = await this.clone(cloneOptions);
        if (!cloneResult.success) {
            return cloneResult;
        }
        try {
            // Install dependencies
            if (installDependencies) {
                await this.installDependencies(cloneResult.outputPath);
            }
            // Run setup commands
            if (runSetup && setupCommands.length > 0) {
                await this.runSetupCommands(cloneResult.outputPath, setupCommands);
            }
            return cloneResult;
        }
        catch (error) {
            return {
                success: false,
                outputPath: cloneResult.outputPath,
                error: `Setup failed: ${error}`,
            };
        }
    }
    /**
     * Install dependencies in cloned repository
     */
    static async installDependencies(repoPath) {
        try {
            // Check if package.json exists
            const packageJsonPath = path.join(repoPath, "package.json");
            await fs.access(packageJsonPath);
            // Install dependencies
            await execAsync("npm install", { cwd: repoPath });
        }
        catch {
            // No package.json or install failed, skip
        }
    }
    /**
     * Run setup commands in cloned repository
     */
    static async runSetupCommands(repoPath, commands) {
        for (const command of commands) {
            try {
                await execAsync(command, { cwd: repoPath });
            }
            catch (error) {
                console.warn(chalk_1.default.yellow(`  ⚠️  Setup command failed: ${command}`));
            }
        }
    }
    /**
     * Clean up cloned repository
     */
    static async cleanup(outputPath) {
        try {
            await fs.rm(outputPath, { recursive: true, force: true });
        }
        catch {
            // Ignore cleanup errors
        }
    }
}
exports.GitHubCloner = GitHubCloner;
//# sourceMappingURL=githubCloner.js.map