/**
 * Judge service for evaluating AI-generated applications.
 *
 * Encapsulates the complete judge evaluation workflow including:
 * - LLM client access for executing evaluations
 * - Storage for persisting evaluation results
 * - MCP server lifecycle management
 * - Executor orchestration
 */
import { JudgeExecutor } from "./judge-executor.js";
import { buildCriteriaFromPrompts } from "./prompts/system-prompt.js";
/**
 * Judge service for orchestrating application evaluations.
 *
 * Provides a high-level API for running judge evaluations
 * and managing their lifecycle.
 */
export class JudgeService {
    llmClient;
    llmProvider;
    storage;
    mcpServerManager;
    logger;
    constructor(config) {
        this.llmClient = config.llmClient;
        this.llmProvider = config.llmProvider;
        this.storage = config.storage;
        this.mcpServerManager = config.mcpServerManager;
        this.logger = config.logger;
    }
    /**
     * Evaluates an application against a prompt.
     *
     * This is the main entry point for judge evaluations. It:
     * 1. Ensures MCP server is running
     * 2. Generates or uses provided criteria
     * 3. Executes the evaluation via JudgeExecutor
     * 4. Stores the result
     * 5. Returns the evaluation
     *
     * @param promptId - Unique identifier for the prompt
     * @param prompt - The prompt text that was used to generate the app
     * @param appId - Application identifier
     * @param options - Evaluation options
     * @returns Complete evaluation result
     * @throws Error if evaluation fails
     */
    async evaluateApplication(promptId, prompt, appId, options) {
        this.logger.info(`Starting judge evaluation: promptId=${promptId}, appId=${appId}, appUrl=${options.appUrl}`);
        try {
            const _useExternalServer = !!options.playwrightServerUrl;
            let mcpUrl = options.playwrightServerUrl;
            if (!mcpUrl) {
                mcpUrl = await this.ensureMcpServer(options.storageStatePath, options.playwrightBrowser, options.playwrightHeadless, options.jwt, options.connectWsEndpoint, options.storageStateData, options.appUrl);
            }
            // Generate criteria if not provided
            const criteria = options.criteria ||
                buildCriteriaFromPrompts([prompt], options.complexity || "medium");
            // Select model based on complexity - create LanguageModel instance from provider
            const model = this.llmProvider.modelForTask("generateBroadEdit");
            // Build judge config
            const judgeConfig = {
                ...options.config,
                playwrightMcpUrl: mcpUrl,
                jwt: options.jwt,
            };
            // Create executor
            const executor = new JudgeExecutor(this.llmClient, model, this.logger, judgeConfig);
            // Initialize executor
            await executor.initialize();
            try {
                // Execute evaluation
                // Note: JudgeExecutor expects a simulation result, but we'll adapt it
                const simulationResult = {
                    promptId,
                    success: true, // Assume success since we're evaluating the final result
                    stepResults: [{ prompt }],
                    tokens: { input: 0, output: 0, total: 0 },
                    duration: 0,
                };
                const evaluation = await executor.evaluateSimulation(simulationResult, criteria, options.appUrl);
                // Get current git metadata for tracking
                const branchName = await this.getCurrentBranch();
                const commitSha = await this.getCurrentCommitSha();
                // Store evaluation
                const storedEvaluation = {
                    timestamp: new Date().toISOString(),
                    promptId,
                    branchName,
                    commitSha,
                    appId,
                    prompt,
                    evaluation,
                };
                await this.storage.saveEvaluation(storedEvaluation);
                this.logger.info(`Judge evaluation completed and stored: promptId=${promptId}, appId=${appId}, score=${evaluation.overallScore}, passed=${evaluation.passed}`);
                // Return result with metadata
                return {
                    ...evaluation,
                    promptId,
                    appId,
                };
            }
            finally {
                // Always cleanup executor
                await executor.cleanup();
            }
        }
        catch (error) {
            this.logger.error(`Judge evaluation failed: ${String(error)}`, {
                error: {
                    kind: "JudgeEvaluationError",
                    message: String(error),
                    stack: error instanceof Error ? error.stack : undefined,
                },
            });
            throw error;
        }
    }
    /**
     * Retrieves stored evaluations.
     *
     * @param promptId - Optional prompt ID to filter by
     * @returns Array of stored evaluations
     */
    async getEvaluations(promptId) {
        if (promptId) {
            return this.storage.getEvaluationsByPrompt(promptId);
        }
        return this.storage.getEvaluations();
    }
    /**
     * Retrieves evaluations for a specific branch.
     *
     * @param branchName - Git branch name
     * @returns Array of stored evaluations for the branch
     */
    async getEvaluationsByBranch(branchName) {
        return this.storage.getEvaluationsByBranch(branchName);
    }
    /**
     * Ensures the MCP server is running.
     *
     * Starts the server if not already running.
     *
     * @param storageStatePath - Optional path to Playwright storage state for authentication
     * @returns MCP server URL
     */
    async ensureMcpServer(storageStatePath, browser = "chromium", headless = true, jwt, connectWsEndpoint, storageStateData, appUrl, sessionStorageData, extraOrigins) {
        if (!this.mcpServerManager.isRunning()) {
            this.logger.info(`Starting Playwright MCP server for judge`, storageStatePath
                ? `with storage state: ${storageStatePath}`
                : "without storage state");
            return await this.mcpServerManager.start({
                browser,
                headless,
                storageStatePath,
                jwt,
                connectWsEndpoint,
                storageStateData,
                appUrl,
                sessionStorageData,
                extraOrigins,
            });
        }
        const url = this.mcpServerManager.getUrl();
        if (!url) {
            throw new Error("MCP server is running but URL is not available");
        }
        return url;
    }
    /**
     * Gets the current git branch name.
     *
     * @returns Branch name or 'unknown' if git is not available
     */
    async getCurrentBranch() {
        try {
            const { execSync } = await import("child_process");
            const branch = execSync("git rev-parse --abbrev-ref HEAD", {
                encoding: "utf-8",
                cwd: process.cwd(),
            }).trim();
            return branch;
        }
        catch {
            return "unknown";
        }
    }
    /**
     * Gets the current git commit SHA.
     *
     * @returns Commit SHA or 'unknown' if git is not available
     */
    async getCurrentCommitSha() {
        try {
            const { execSync } = await import("child_process");
            const sha = execSync("git rev-parse HEAD", {
                encoding: "utf-8",
                cwd: process.cwd(),
            }).trim();
            return sha;
        }
        catch {
            return "unknown";
        }
    }
    /**
     * Cleans up resources used by the judge service.
     *
     * Stops the MCP server if running.
     */
    async dispose() {
        this.logger.info("Disposing judge service");
        if (this.mcpServerManager.isRunning()) {
            await this.mcpServerManager.stop();
        }
    }
}
//# sourceMappingURL=judge-service.js.map