import { randomUUID } from "node:crypto";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { DEFAULT_CLAUDE_CODE_MODEL, DEFAULT_CODEX_MODEL } from "../cli/constants.js";
import { hasOwnErrorCode } from "../utils/error-codes.js";
const CLAUDE_DEFAULT_MODEL = DEFAULT_CLAUDE_CODE_MODEL;
/**
 * Available model identifiers
 */
export const AVAILABLE_MODELS = Object.freeze([
    CLAUDE_DEFAULT_MODEL,
    "gpt-5.5",
    "gpt-5.4",
    "gpt-5.4-chat",
    "gpt-5.4-pro",
    "gpt-4o",
    "Claude-3.5-Sonnet",
    DEFAULT_CODEX_MODEL,
]);
/**
 * Mixed strategy: alternates between gpt-5.5 and the default Claude model
 */
export class MixedStrategy {
    currentIndex = 0;
    models = ["gpt-5.5", CLAUDE_DEFAULT_MODEL];
    getNextModel() {
        const model = this.models[this.currentIndex];
        this.currentIndex = (this.currentIndex + 1) % this.models.length;
        return model;
    }
    getName() {
        return "mixed";
    }
    getDescription() {
        return `Alternates between gpt-5.5 and ${CLAUDE_DEFAULT_MODEL} on each call`;
    }
    reset() {
        this.currentIndex = 0;
    }
}
/**
 * Smart strategy: selects model based on task type
 */
export class SmartStrategy {
    lastModel = CLAUDE_DEFAULT_MODEL;
    getNextModel(context) {
        if (!context) {
            return CLAUDE_DEFAULT_MODEL;
        }
        // Smart selection based on context
        if (context.messageType === "code" || context.messageType === "reasoning") {
            // Use gpt-5.5 for complex coding and reasoning tasks
            if (context.complexity === "complex") {
                this.lastModel = "gpt-5.5";
                return "gpt-5.5";
            }
            // Use Claude for medium complexity code
            this.lastModel = CLAUDE_DEFAULT_MODEL;
            return CLAUDE_DEFAULT_MODEL;
        }
        if (context.messageType === "chat") {
            // Use gpt-4o for general chat
            this.lastModel = "gpt-4o";
            return "gpt-4o";
        }
        // Default to Claude
        this.lastModel = CLAUDE_DEFAULT_MODEL;
        return CLAUDE_DEFAULT_MODEL;
    }
    getName() {
        return "smart";
    }
    getDescription() {
        return "Intelligently selects model based on task complexity and type";
    }
    reset() {
        this.lastModel = CLAUDE_DEFAULT_MODEL;
    }
}
/**
 * Fixed strategy: always uses the same model
 */
export class FixedStrategy {
    model;
    constructor(model = CLAUDE_DEFAULT_MODEL) {
        this.model = model;
    }
    getNextModel() {
        return this.model;
    }
    setModel(model) {
        this.model = model;
    }
    getName() {
        return "fixed";
    }
    getDescription() {
        return `Always uses ${this.model}`;
    }
    reset() {
        // No state to reset
    }
}
/**
 * Round-robin strategy: cycles through all available models
 */
export class RoundRobinStrategy {
    currentIndex = 0;
    models;
    constructor(models) {
        if (models?.length === 0) {
            throw new Error("Round-robin custom order must include at least one model");
        }
        this.models = models ? [...models] : [...AVAILABLE_MODELS];
    }
    getNextModel() {
        const model = this.models[this.currentIndex];
        this.currentIndex = (this.currentIndex + 1) % this.models.length;
        return model;
    }
    getName() {
        return "round-robin";
    }
    getDescription() {
        return `Cycles through: ${this.models.join(", ")}`;
    }
    reset() {
        this.currentIndex = 0;
    }
}
/**
 * Factory for creating model strategies
 */
export class ModelStrategyFactory {
    static createStrategy(config) {
        switch (config.type) {
            case "mixed":
                return new MixedStrategy();
            case "smart":
                return new SmartStrategy();
            case "fixed":
                return new FixedStrategy(config.fixedModel);
            case "round-robin":
                return new RoundRobinStrategy(config.customOrder);
            default:
                return new MixedStrategy();
        }
    }
    static getAvailableStrategies() {
        return [
            {
                type: "mixed",
                description: `Alternate between gpt-5.5 and ${CLAUDE_DEFAULT_MODEL}`
            },
            { type: "smart", description: "Intelligently select based on task type" },
            { type: "fixed", description: "Always use the same model" },
            { type: "round-robin", description: "Cycle through all available models" },
        ];
    }
}
/**
 * Manager for persisting and loading strategy configuration
 */
export class StrategyConfigManager {
    static CONFIG_DIR = path.join(os.homedir(), ".poe-code");
    static CONFIG_FILE = path.join(StrategyConfigManager.CONFIG_DIR, "strategy-config.json");
    static saveConfig(config) {
        if (!isStrategyConfig(config)) {
            throw new Error("Invalid model strategy configuration");
        }
        this.assertSafeStatePath();
        if (!fs.existsSync(this.CONFIG_DIR)) {
            fs.mkdirSync(this.CONFIG_DIR, { recursive: true });
        }
        this.assertSafeStatePath();
        const temporaryFile = `${this.CONFIG_FILE}.${process.pid}.${randomUUID()}.tmp`;
        let temporaryCreated = false;
        try {
            fs.writeFileSync(temporaryFile, JSON.stringify(config, null, 2), { flag: "wx" });
            temporaryCreated = true;
            fs.renameSync(temporaryFile, this.CONFIG_FILE);
            temporaryCreated = false;
        }
        catch (error) {
            if (temporaryCreated || !isAlreadyExists(error)) {
                tryUnlinkSync(temporaryFile);
            }
            throw error;
        }
    }
    static loadConfig() {
        try {
            this.assertSafeStatePath();
            if (fs.existsSync(this.CONFIG_FILE)) {
                const data = fs.readFileSync(this.CONFIG_FILE, "utf-8");
                const config = JSON.parse(data);
                return isStrategyConfig(config) ? config : null;
            }
        }
        catch (error) {
            console.error("Failed to load strategy config:", error);
        }
        return null;
    }
    static getDefaultConfig() {
        return {
            type: "fixed",
            fixedModel: CLAUDE_DEFAULT_MODEL,
        };
    }
    static assertSafeStatePath() {
        for (const candidate of [this.CONFIG_DIR, this.CONFIG_FILE]) {
            if (fs.existsSync(candidate) && fs.lstatSync(candidate).isSymbolicLink()) {
                throw new Error(`Strategy config path cannot be a symbolic link: ${candidate}`);
            }
        }
    }
}
function isAlreadyExists(error) {
    return hasOwnErrorCode(error, "EEXIST");
}
function tryUnlinkSync(filePath) {
    try {
        fs.unlinkSync(filePath);
    }
    catch {
        return;
    }
}
function isStrategyConfig(value) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return false;
    }
    const config = value;
    if (!isStrategyType(config.type)) {
        return false;
    }
    if (config.fixedModel !== undefined && !isModelIdentifier(config.fixedModel)) {
        return false;
    }
    if (config.customOrder !== undefined) {
        if (!Array.isArray(config.customOrder) ||
            config.customOrder.length === 0 ||
            !config.customOrder.every(isModelIdentifier)) {
            return false;
        }
    }
    return true;
}
function isStrategyType(value) {
    return value === "mixed" || value === "smart" || value === "fixed" || value === "round-robin";
}
function isModelIdentifier(value) {
    return typeof value === "string" && AVAILABLE_MODELS.includes(value);
}
//# sourceMappingURL=model-strategy.js.map