// ============================================================================
// Import
// ============================================================================

import { promises as fs } from "fs";
import path from "path";
import postcss from "postcss";
import * as sass from "sass";

import { Action } from "../../core/pipeline/Action.js";
import { ActionOptionsType } from "../../types/ActionOptionsType.js";

// Assuming the PostCSS configurations are available at the given paths
import postcssConfigCompressed from "./postcss.config.compressed.js";
import postcssConfigExpanded from "./postcss.config.expanded.js";

// ============================================================================
// Classes
// ============================================================================

/**
 * StyleProcessingAction is a step action responsible for processing styles,
 * including compiling SCSS and applying PostCSS transformations. It supports
 * expanded and compressed output styles based on the provided configuration.
 */
export class StyleProcessingAction extends Action {
    // Parameters
    // ========================================================================

    // Constructor
    // ========================================================================

    // Methods
    // ========================================================================

    /**
     * Executes the style processing action.
     * @param options - The options specific to style processing, including
     * input/output file paths and style format.
     * @returns A Promise that resolves when the styles are processed
     * successfully, or rejects with an error if the action fails.
     */
    async execute(options: ActionOptionsType): Promise<void> {
        const inputFile = options.inputFile as string;
        const outputFile = options.outputFile as string;
        const styleOption = options.styleOption as "expanded" | "compressed";

        if (!inputFile || !outputFile || !styleOption) {
            throw new Error(
                "Missing required options: inputFile, outputFile, or styleOption.",
            );
        }

        this.logInfo(
            `Processing styles from ${inputFile} to ${outputFile} with ${styleOption} style.`,
        );

        try {
            // Ensure the output directory exists
            const outputDir = path.dirname(outputFile);
            await this.ensureDirectoryExists(outputDir);

            // Compile SCSS to CSS
            const result = await sass.compileAsync(inputFile, {
                style: styleOption,
                importers: [new sass.NodePackageImporter()],
            });

            // Process the compiled CSS with PostCSS
            const processedCss = await this.processPostCSS(
                result.css,
                styleOption,
            );

            // Write the processed CSS to a file
            await fs.writeFile(outputFile, processedCss, "utf-8");

            this.logInfo(
                `Styles processed successfully from ${inputFile} to ${outputFile}.`,
            );
        } catch (error) {
            this.logError(
                `Error processing styles from ${inputFile}: ${error}`,
            );
            throw error;
        }
    }

    /**
     * Processes the given CSS with PostCSS based on the provided style option.
     * @param css - The CSS string to process.
     * @param styleOption - The style option, either "expanded" or "compressed".
     * @returns Processed CSS string.
     */
    private async processPostCSS(
        css: string,
        styleOption: "expanded" | "compressed",
    ): Promise<string> {
        const config =
            styleOption === "expanded"
                ? postcssConfigExpanded
                : postcssConfigCompressed;
        const result = await postcss(config.plugins).process(css, {
            from: undefined,
            map: { inline: false },
        });
        return result.css;
    }

    /**
     * Ensures that the given directory exists, creating it if it does not.
     * @param dirPath - The path of the directory to check and create.
     */
    private async ensureDirectoryExists(dirPath: string): Promise<void> {
        try {
            await fs.mkdir(dirPath, { recursive: true });
        } catch (error) {
            if (error instanceof Error) {
                const nodeError = error as NodeJS.ErrnoException;
                if (nodeError.code !== "EEXIST") {
                    throw nodeError;
                }
            } else {
                throw error;
            }
        }
    }

    /**
     * Provides a description of the action.
     * @returns A string description of the action.
     */
    describe(): string {
        return "Processes SCSS files into CSS, applying PostCSS transformations for expanded or compressed outputs.";
    }
}

// ============================================================================
// Export
// ============================================================================

// export default StyleProcessingAction;
