import { MatugenColors } from '../lib/options/types';
import { initializeTrackers } from './optionsTrackers';
import { readFile, writeFile } from 'astal/file';
import { App } from 'astal/gtk3';
import { initializeHotReload } from './utils/hotReload';
import { Opt } from 'src/lib/options';
import { SystemUtilities } from 'src/core/system/SystemUtilities';
import options from 'src/configuration';
import { MatugenService } from 'src/services/matugen';
import { isHexColor } from 'src/lib/validation/colors';

const matugenService = MatugenService.getInstance();

/**
 * Central manager for theme styling throughout the application
 * Handles the transformation of theme options into compiled CSS
 */
class ThemeStyleManager {
    /**
     * Orchestrates the full theme regeneration process
     * Falls back to standard theme if Matugen is unavailable
     */
    public async applyCss(): Promise<void> {
        if (!SystemUtilities.checkDependencies('sass')) return;

        try {
            const variables = await this._generateThemeVariables();

            await this._compileSass(variables);

            this._applyCss();
        } catch (error) {
            console.error(error);
        }
    }

    /**
     * Decides whether to use Matugen-generated colors or standard theme variables
     * Controls the main theming strategy based on user configuration
     *
     * @returns An array of SCSS variable declarations
     */
    private async _generateThemeVariables(): Promise<string[]> {
        const useMatugen = options.theme.matugen.get();
        if (!useMatugen) {
            return this._extractStandardVariables();
        }

        const matugenColors = await matugenService.generateMatugenColors();
        if (!matugenColors) {
            return this._extractStandardVariables();
        }

        return this._extractMatugenizedVariables(matugenColors);
    }

    /**
     * Recursively processes theme objects to generate SCSS variables
     * Handles nested properties by creating properly namespaced variable names
     *
     * @returns An array of SCSS variable declarations using standard theme values
     */
    private _extractStandardVariables(): string[] {
        const cssVariables: string[] = [];

        const optArray = options.toArray();

        for (const opt of optArray) {
            const currentPath = opt.id;

            if (!currentPath.startsWith('theme.')) {
                continue;
            }

            const variableName = this._buildCssVariableName(currentPath);
            const variable = this._buildCssVariable(variableName, opt);

            cssVariables.push(variable);
        }

        return cssVariables;
    }

    /**
     * Alternative variable extraction when using Matugen's color generation
     * Processes all theme options and applies Matugen's palette where appropriate
     *
     * @param matugenColors - Color palette generated by Matugen service
     * @returns An array of SCSS variable declarations with Matugen colors applied
     */
    private async _extractMatugenizedVariables(matugenColors: MatugenColors): Promise<string[]> {
        try {
            const result: string[] = [];
            const optArray = options.toArray();

            for (const opt of optArray) {
                const currentPath = opt.id;

                if (!currentPath.startsWith('theme.')) {
                    continue;
                }

                const optionValue = opt.get();
                const variableName = this._buildCssVariableName(currentPath);

                if (!isHexColor(optionValue)) {
                    result.push(`$${variableName}: ${optionValue};`);
                    continue;
                }

                const defaultThemeValue = opt.initial;

                if (!isHexColor(defaultThemeValue)) {
                    continue;
                }

                const matugenColor = matugenService.getMatugenHex(defaultThemeValue, matugenColors);
                result.push(`$${variableName}: ${matugenColor};`);
            }

            return result;
        } catch (error) {
            console.error(error);
            return [];
        }
    }

    /**
     * Handles object properties that have values needing transformation
     * Creates properly formatted SCSS variable declarations
     *
     * @param variableName - CSS-friendly variable name
     * @param property - Option object containing the property value
     * @returns Formatted SCSS variable declaration
     */
    private _buildCssVariable(variableName: string, property: Opt): string {
        const propertyValue = property.get();

        return `$${variableName}: ${propertyValue};`;
    }

    /**
     * Transforms dotted paths into hyphenated CSS variable names
     * Strips the "theme." prefix for cleaner variable naming
     *
     * @param path - Dot-notation path of an option (e.g., "theme.background.primary")
     * @returns CSS-friendly variable name (e.g., "background-primary")
     */
    private _buildCssVariableName(path: string): string {
        return path.replace('theme.', '').split('.').join('-');
    }

    /**
     * Executes the SCSS compilation process with generated variables
     * Combines main SCSS with custom variables and module styles
     *
     * @param themeVariables - Array of SCSS variable declarations for user customization options
     *
     * File paths used in compilation:
     * - themeVariablesPath: Contains all user-configurable variables (theme colors, margins, borders, etc.)
     * - appScssPath: The application's main SCSS entry point file
     * - entryScssPath: A temporary file that combines all SCSS sources in the correct order
     * - modulesScssPath: User-defined custom module styles
     * - compiledCssPath: The final compiled CSS that gets used by the application
     */
    private async _compileSass(themeVariables: string[]): Promise<void> {
        const themeVariablesPath = `${TMP}/variables.scss`;
        const appScssPath = `${SRC_DIR}/src/style/main.scss`;
        const entryScssPath = `${TMP}/entry.scss`;
        const modulesScssPath = `${CONFIG_DIR}/modules.scss`;
        const compiledCssPath = `${TMP}/main.css`;

        const scssImports = [`@import '${themeVariablesPath}';`];

        writeFile(themeVariablesPath, themeVariables.join('\n'));

        let combinedScss = readFile(appScssPath);
        combinedScss = `${scssImports.join('\n')}\n${combinedScss}`;

        const moduleCustomizations = readFile(modulesScssPath);
        combinedScss = `${combinedScss}\n${moduleCustomizations}`;

        writeFile(entryScssPath, combinedScss);

        await SystemUtilities.bash(
            `sass --load-path=${SRC_DIR}/src/style ${entryScssPath} ${compiledCssPath}`,
        );
    }

    /**
     * Loads the compiled CSS into the application
     *
     * @remarks
     * Uses the compiled CSS file generated in _compileSass to apply styles to the application
     */
    private _applyCss(): void {
        const compiledCssPath = `${TMP}/main.css`;

        App.apply_css(compiledCssPath, true);
    }
}

const themeManager = new ThemeStyleManager();
const optionsToWatch = [
    'font',
    'theme',
    'bar.flatButtons',
    'bar.position',
    'bar.battery.charging',
    'bar.battery.blocks',
];

initializeTrackers(themeManager.applyCss.bind(themeManager));
initializeHotReload();

options.handler(optionsToWatch, themeManager.applyCss.bind(themeManager));

await themeManager.applyCss();

export { themeManager };
