/**
 * QCObjects Framework CLI
 * ________________
 *
 * Author: Jean Machuca <correojean@gmail.com>
 *
 * Cross Browser Javascript Framework for MVC Patterns
 * QCObjects is licensed under the MIT License
 * [LICENSE] (https://github.com/QCObjects/qcobjects-cli/blob/main/LICENSE.txt)
 *
 * Copyright (C) 2015 Jean Machuca,<correojean@gmail.com>
 *
 * Everyone is permitted to copy and distribute verbatim copies of this
 * license document, but changing it is not allowed.
 */
/*eslint no-unused-vars: "off"*/
/*eslint no-redeclare: "off"*/
/*eslint no-empty: "off"*/
/*eslint strict: "off"*/
/*eslint no-mixed-operators: "off"*/
/*eslint no-undef: "off"*/
"use strict";
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import glob from "glob";
import { Linter } from "eslint";
import globals from "globals";
import * as tsParserModule from "@typescript-eslint/parser";
import * as tsPluginModule from "@typescript-eslint/eslint-plugin";
import { Package, InheritClass } from "qcobjects";

const tsParser = (tsParserModule as any).default ?? tsParserModule;
const tsPlugin = (tsPluginModule as any).default ?? tsPluginModule;

const DEFAULT_TARGETS = ["src/**/*.ts"];

const DEFAULT_IGNORES = [
  "node_modules/**",
  "build/**",
  "public/**",
  "dist/**",
  "coverage/**",
  ".git/**",
];

const DEFAULT_FLAT_CONFIG: any[] = [
  {
    files: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.mjs", "**/*.cjs"],
    plugins: {
      "@typescript-eslint": tsPlugin,
    },
    languageOptions: {
      parser: tsParser,
      ecmaVersion: "latest",
      sourceType: "module",
      globals: {
        ...globals.browser,
        ...globals.node,
        ...globals.commonjs,
      },
    },
    rules: {
      "no-undef": "error",
      "no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
      "no-empty": "off",
      "no-constant-condition": "off",
      "no-redeclare": "off",
      "no-var": "off",
      "no-mixed-operators": "off",
    },
  },
];

export class CommandHandler extends InheritClass {
  choiceOption: {
    lint: (targets: string[], options: { fix?: boolean; config?: string }) => Promise<void>;
  };

  constructor({
    switchCommander
  }: { switchCommander: any }) {
    super({ switchCommander });
    this.choiceOption = {
      async lint(targets, options) {
        try {
          const cwd = process.cwd();
          const requestedTargets = targets.length > 0 ? targets : DEFAULT_TARGETS;
          const ignoreFlags = DEFAULT_IGNORES.map(ignored => new RegExp(ignored.replace(/\*\*/g, ".*")));
          const files = requestedTargets
            .flatMap(target => glob.sync(target, { cwd, nodir: true }))
            .filter((file: string) => !ignoreFlags.some(flag => flag.test(file)));

          if (files.length === 0) {
            console.warn(`No files matched: ${requestedTargets.join(", ")}`);
            process.exit(0);
          }

          let flatConfig = DEFAULT_FLAT_CONFIG;
          const appConfigPath = ["eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"]
            .map(name => path.resolve(cwd, name))
            .find(appConfigPath => fs.existsSync(appConfigPath));

          if (options.config) {
            const explicitConfigPath = path.resolve(cwd, options.config);
            if (!fs.existsSync(explicitConfigPath)) {
              console.warn(`Config file not found: ${explicitConfigPath}`);
              process.exit(1);
            }
            flatConfig = (await import(pathToFileURL(explicitConfigPath).href)).default;
            console.log(`Using config file: ${explicitConfigPath}`);
          } else if (appConfigPath) {
            flatConfig = (await import(pathToFileURL(appConfigPath).href)).default;
            console.log(`Using app config file: ${appConfigPath}`);
          } else if (fs.existsSync(path.resolve(cwd, ".eslintrc")) || fs.existsSync(path.resolve(cwd, ".eslintrc.js")) || fs.existsSync(path.resolve(cwd, ".eslintrc.cjs")) || fs.existsSync(path.resolve(cwd, ".eslintrc.json"))) {
            console.warn("Legacy .eslintrc config detected but not supported; using built-in QCObjects settings. Migrate to eslint.config.mjs to override defaults.");
          } else {
            console.log("No app eslint config found. Using built-in QCObjects settings (overridable with an eslint.config.mjs in your app).");
          }

          const linter = new Linter({ cwd, configType: "flat" });
          let errorCount = 0;
          let warningCount = 0;
          let fixedCount = 0;

          for (const file of files) {
            const absolutePath = path.resolve(cwd, file);
            const source = fs.readFileSync(absolutePath, "utf8");
            const result: { fixed: boolean; output: string; messages: any[] } = options.fix
              ? linter.verifyAndFix(source, flatConfig, absolutePath)
              : { fixed: false, output: source, messages: linter.verify(source, flatConfig, absolutePath) };
            if (result.fixed) {
              fixedCount++;
              fs.writeFileSync(absolutePath, result.output);
            }
            for (const message of result.messages) {
              const severity = message.severity === 2 ? "error" : message.severity === 1 ? "warning" : "info";
              const output = `${file}:${message.line}:${message.column} ${severity} ${message.message} (${message.ruleId ?? "parse"})`;
              if (message.severity === 2) {
                errorCount++;
                console.error(output);
              } else if (message.severity === 1) {
                warningCount++;
                console.warn(output);
              } else {
                console.log(output);
              }
            }
          }

          const summary = `${files.length} file(s) linted, ${errorCount} error(s), ${warningCount} warning(s), ${fixedCount} file(s) fixed.`;
          if (errorCount > 0) {
            console.error(summary);
            process.exit(1);
          }
          console.log(summary);
          process.exit(0);
        } catch (e: any) {
          console.warn(`Something went wrong trying to lint: ${e.message}`);
          process.exit(1);
        }
      }
    };

    const commandHandler = this;

    const lintCommand = switchCommander.program.command("lint [targets...]")
      .allowExcessArguments(false)
      .option("-f, --fix", "Automatically fix problems")
      .option("-c, --config <path>", "Use a specific eslint config file")
      .description("Lints JS/TS files using built-in QCObjects settings. Default target: src/**/*.ts")
      .action(function (targets: string[], options: { fix?: boolean; config?: string }) {
        commandHandler.choiceOption.lint.call(commandHandler, targets ?? [], options);
      });

    }
}

Package("com.qcobjects.cli.commands.lint", [
  CommandHandler
]);