/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { AyuDark } from './ayu.js';
import { AyuLight } from './ayu-light.js';
import { AtomOneDark } from './atom-one-dark.js';
import { Dracula } from './dracula.js';
import { GitHubDark } from './github-dark.js';
import { GitHubLight } from './github-light.js';
import { GoogleCode } from './googlecode.js';
import { DefaultLight } from './default-light.js';
import { DefaultDark } from './default.js';
import { ShadesOfPurple } from './shades-of-purple.js';
import { XCode } from './xcode.js';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { createCustomTheme, validateCustomTheme } from './theme.js';
import { ANSI } from './ansi.js';
import { ANSILight } from './ansi-light.js';
import { NoColorTheme } from './no-color.js';
import process from 'node:process';
export const DEFAULT_THEME = DefaultDark;
class ThemeManager {
    availableThemes;
    activeTheme;
    customThemes = new Map();
    constructor() {
        this.availableThemes = [
            AyuDark,
            AyuLight,
            AtomOneDark,
            Dracula,
            DefaultLight,
            DefaultDark,
            GitHubDark,
            GitHubLight,
            GoogleCode,
            ShadesOfPurple,
            XCode,
            ANSI,
            ANSILight,
        ];
        this.activeTheme = DEFAULT_THEME;
    }
    /**
     * Loads custom themes from settings.
     * @param customThemesSettings Custom themes from settings.
     */
    loadCustomThemes(customThemesSettings) {
        this.customThemes.clear();
        if (!customThemesSettings) {
            return;
        }
        for (const [name, customThemeConfig] of Object.entries(customThemesSettings)) {
            const validation = validateCustomTheme(customThemeConfig);
            if (validation.isValid) {
                if (validation.warning) {
                    console.warn(`Theme "${name}": ${validation.warning}`);
                }
                const themeWithDefaults = {
                    ...DEFAULT_THEME.colors,
                    ...customThemeConfig,
                    name: customThemeConfig.name || name,
                    type: 'custom',
                };
                try {
                    const theme = createCustomTheme(themeWithDefaults);
                    this.customThemes.set(name, theme);
                }
                catch (error) {
                    console.warn(`Failed to load custom theme "${name}":`, error);
                }
            }
            else {
                console.warn(`Invalid custom theme "${name}": ${validation.error}`);
            }
        }
        // If the current active theme is a custom theme, keep it if still valid
        if (this.activeTheme &&
            this.activeTheme.type === 'custom' &&
            this.customThemes.has(this.activeTheme.name)) {
            this.activeTheme = this.customThemes.get(this.activeTheme.name);
        }
    }
    /**
     * Sets the active theme.
     * @param themeName The name of the theme to set as active.
     * @returns True if the theme was successfully set, false otherwise.
     */
    setActiveTheme(themeName) {
        const theme = this.findThemeByName(themeName);
        if (!theme) {
            return false;
        }
        this.activeTheme = theme;
        return true;
    }
    /**
     * Gets the currently active theme.
     * @returns The active theme.
     */
    getActiveTheme() {
        if (process.env['NO_COLOR']) {
            return NoColorTheme;
        }
        if (this.activeTheme) {
            const isBuiltIn = this.availableThemes.some((t) => t.name === this.activeTheme.name);
            const isCustom = [...this.customThemes.values()].includes(this.activeTheme);
            if (isBuiltIn || isCustom) {
                return this.activeTheme;
            }
        }
        // Fallback to default if no active theme or if it's no longer valid.
        this.activeTheme = DEFAULT_THEME;
        return this.activeTheme;
    }
    /**
     * Gets the semantic colors for the active theme.
     * @returns The semantic colors.
     */
    getSemanticColors() {
        return this.getActiveTheme().semanticColors;
    }
    /**
     * Gets a list of custom theme names.
     * @returns Array of custom theme names.
     */
    getCustomThemeNames() {
        return Array.from(this.customThemes.keys());
    }
    /**
     * Checks if a theme name is a custom theme.
     * @param themeName The theme name to check.
     * @returns True if the theme is custom.
     */
    isCustomTheme(themeName) {
        return this.customThemes.has(themeName);
    }
    /**
     * Returns a list of available theme names.
     */
    getAvailableThemes() {
        const builtInThemes = this.availableThemes.map((theme) => ({
            name: theme.name,
            type: theme.type,
            isCustom: false,
        }));
        const customThemes = Array.from(this.customThemes.values()).map((theme) => ({
            name: theme.name,
            type: theme.type,
            isCustom: true,
        }));
        const allThemes = [...builtInThemes, ...customThemes];
        const sortedThemes = allThemes.sort((a, b) => {
            const typeOrder = (type) => {
                switch (type) {
                    case 'dark':
                        return 1;
                    case 'light':
                        return 2;
                    case 'ansi':
                        return 3;
                    case 'custom':
                        return 4; // Custom themes at the end
                    default:
                        return 5;
                }
            };
            const typeComparison = typeOrder(a.type) - typeOrder(b.type);
            if (typeComparison !== 0) {
                return typeComparison;
            }
            return a.name.localeCompare(b.name);
        });
        return sortedThemes;
    }
    /**
     * Gets a theme by name.
     * @param themeName The name of the theme to get.
     * @returns The theme if found, undefined otherwise.
     */
    getTheme(themeName) {
        return this.findThemeByName(themeName);
    }
    isPath(themeName) {
        return (themeName.endsWith('.json') ||
            themeName.startsWith('.') ||
            path.isAbsolute(themeName));
    }
    loadThemeFromFile(themePath) {
        try {
            // realpathSync resolves the path and throws if it doesn't exist.
            const canonicalPath = fs.realpathSync(path.resolve(themePath));
            // 1. Check cache using the canonical path.
            if (this.customThemes.has(canonicalPath)) {
                return this.customThemes.get(canonicalPath);
            }
            // 2. Perform security check.
            const homeDir = path.resolve(os.homedir());
            if (!canonicalPath.startsWith(homeDir)) {
                console.warn(`Theme file at "${themePath}" is outside your home directory. ` +
                    `Only load themes from trusted sources.`);
                return undefined;
            }
            // 3. Read, parse, and validate the theme file.
            const themeContent = fs.readFileSync(canonicalPath, 'utf-8');
            const customThemeConfig = JSON.parse(themeContent);
            const validation = validateCustomTheme(customThemeConfig);
            if (!validation.isValid) {
                console.warn(`Invalid custom theme from file "${themePath}": ${validation.error}`);
                return undefined;
            }
            if (validation.warning) {
                console.warn(`Theme from "${themePath}": ${validation.warning}`);
            }
            // 4. Create and cache the theme.
            const themeWithDefaults = {
                ...DEFAULT_THEME.colors,
                ...customThemeConfig,
                name: customThemeConfig.name || canonicalPath,
                type: 'custom',
            };
            const theme = createCustomTheme(themeWithDefaults);
            this.customThemes.set(canonicalPath, theme); // Cache by canonical path
            return theme;
        }
        catch (error) {
            // Any error in the process (file not found, bad JSON, etc.) is caught here.
            // We can return undefined silently for file-not-found, and warn for others.
            if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) {
                console.warn(`Could not load theme from file "${themePath}":`, error);
            }
            return undefined;
        }
    }
    findThemeByName(themeName) {
        if (!themeName) {
            return DEFAULT_THEME;
        }
        // First check built-in themes
        const builtInTheme = this.availableThemes.find((theme) => theme.name === themeName);
        if (builtInTheme) {
            return builtInTheme;
        }
        // Then check custom themes that have been loaded from settings, or file paths
        if (this.isPath(themeName)) {
            return this.loadThemeFromFile(themeName);
        }
        if (this.customThemes.has(themeName)) {
            return this.customThemes.get(themeName);
        }
        // If it's not a built-in, not in cache, and not a valid file path,
        // it's not a valid theme.
        return undefined;
    }
}
// Export an instance of the ThemeManager
export const themeManager = new ThemeManager();
//# sourceMappingURL=theme-manager.js.map