import { Range, Position } from "vscode-languageserver/node";
import { Linter, rules } from "@herb-tools/linter";
import { loadCustomRules as loadCustomRulesFromFs } from "@herb-tools/linter/loader";
import { Herb } from "@herb-tools/node-wasm";
import { Config } from "@herb-tools/config";
import { lintToDignosticSeverity } from "./utils";
const OPEN_CONFIG_ACTION = 'Open .herb.yml';
export class LinterService {
    constructor(connection, settings, project) {
        this.source = "Herb Linter ";
        this.allRules = rules;
        this.customRulesLoaded = false;
        this.failedCustomRules = new Map();
        this.hasShownCustomRuleWarning = false;
        this.customRulePaths = new Map();
        this.connection = connection;
        this.settings = settings;
        this.project = project;
    }
    /**
     * Rebuild the linter when config changes
     * This ensures the linter uses the latest rule configuration
     */
    rebuildLinter() {
        this.linter = undefined;
        this.allRules = rules;
        this.customRulesLoaded = false;
        this.hasShownCustomRuleWarning = false;
        this.failedCustomRules.clear();
        this.customRulePaths.clear();
    }
    /**
     * Load custom rules from the project and merge with built-in rules
     */
    async loadCustomRules() {
        if (this.customRulesLoaded) {
            return;
        }
        const baseDir = this.project.projectPath;
        try {
            const { rules: customRules, ruleInfo, warnings: duplicateWarnings } = await loadCustomRulesFromFs({ baseDir, silent: true });
            if (customRules.length > 0) {
                this.connection.console.log(`[Linter] Loaded ${customRules.length} custom rules: ${ruleInfo.map(r => r.name).join(', ')}`);
                ruleInfo.forEach(({ name, path }) => {
                    this.customRulePaths.set(name, path);
                });
                this.allRules = [...rules, ...customRules];
                if (duplicateWarnings.length > 0) {
                    duplicateWarnings.forEach(warning => {
                        this.connection.console.warn(`[Linter] ${warning}`);
                    });
                }
            }
            this.customRulesLoaded = true;
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            this.connection.console.error(`[Linter] Failed to load custom rules: ${errorMessage}`);
            this.failedCustomRules.set('custom-rules', errorMessage);
            this.customRulesLoaded = true;
        }
    }
    /**
     * Show warning message to user about failed custom rules
     */
    showCustomRuleWarnings() {
        if (this.failedCustomRules.size === 0 || this.hasShownCustomRuleWarning) {
            return;
        }
        this.hasShownCustomRuleWarning = true;
        const failures = Array.from(this.failedCustomRules.entries());
        const message = failures.length === 1
            ? `Failed to load custom linter rules: ${failures[0][1]}`
            : `Failed to load custom linter rules:\n${failures.map(([_, error], i) => `${i + 1}. ${error}`).join('\n')}`;
        if (this.settings.hasShowDocumentCapability) {
            this.connection.window.showWarningMessage(message, { title: OPEN_CONFIG_ACTION }).then(action => {
                if ((action === null || action === void 0 ? void 0 : action.title) === OPEN_CONFIG_ACTION) {
                    const configPath = `${this.project.projectPath}/.herb.yml`;
                    this.connection.window.showDocument({ uri: `file://${configPath}`, takeFocus: true });
                }
            });
        }
        else {
            this.connection.window.showWarningMessage(message);
        }
    }
    async lintDocument(textDocument) {
        var _a, _b, _c, _d;
        const settings = await this.settings.getDocumentSettings(textDocument.uri);
        const linterEnabled = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.linter) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : true;
        if (!linterEnabled) {
            return { diagnostics: [] };
        }
        const projectConfig = this.settings.projectConfig;
        if (!this.linter) {
            await this.loadCustomRules();
            this.showCustomRuleWarnings();
            const linterConfig = ((_c = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.config) === null || _c === void 0 ? void 0 : _c.linter) || { enabled: true, rules: {} };
            const config = Config.fromObject({
                linter: {
                    ...linterConfig,
                    rules: {
                        ...linterConfig.rules,
                        'parser-no-errors': { enabled: false }
                    }
                }
            }, { projectPath: (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.projectPath) || process.cwd() });
            const filteredRules = Linter.filterRulesByConfig(this.allRules, (_d = config.linter) === null || _d === void 0 ? void 0 : _d.rules);
            this.linter = new Linter(Herb, filteredRules, config, this.allRules);
        }
        const content = textDocument.getText();
        const lintResult = this.linter.lint(content, { fileName: textDocument.uri });
        const diagnostics = lintResult.offenses.map(offense => {
            const range = Range.create(Position.create(offense.location.start.line - 1, offense.location.start.column), Position.create(offense.location.end.line - 1, offense.location.end.column));
            const customRulePath = this.customRulePaths.get(offense.rule);
            const codeDescription = {
                href: customRulePath
                    ? `file://${customRulePath}`
                    : `https://herb-tools.dev/linter/rules/${offense.rule}`
            };
            return {
                source: this.source,
                severity: lintToDignosticSeverity(offense.severity),
                range,
                message: offense.message,
                code: offense.rule,
                data: { rule: offense.rule },
                codeDescription
            };
        });
        return { diagnostics };
    }
}
//# sourceMappingURL=linter_service.js.map