package click.kamil.springstatemachineexporter.command;

import click.kamil.springstatemachineexporter.service.ExportService;
import lombok.RequiredArgsConstructor;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.Callable;

@Command(
    name = "spring-sm-exporter",
    mixinStandardHelpOptions = true,
    version = "1.0.0",
    description = "Exports Spring State Machine configurations to UML, DOT, and SCXML using AST analysis.",
    headerHeading = "@|bold,underline Usage:|@%n%n",
    synopsisHeading = "%n",
    descriptionHeading = "%n@|bold,underline Description:|@%n%n",
    parameterListHeading = "%n@|bold,underline Parameters:|@%n",
    optionListHeading = "%n@|bold,underline Options:|@%n",
    header = "Welcome to the @|bold,blue Spring State Machine Renderer|@!%n"
)
@RequiredArgsConstructor
public class ExporterCommand implements Callable<Integer> {

    private final ExportService exportService;

    @CommandLine.Spec
    CommandLine.Model.CommandSpec spec;

    @Option(names = {"-i", "--input"}, description = "Input directory containing the Spring Boot project source code. With -j, optionally rescans sources to resolve machine enum types.")
    private Path inputDir;

    @Option(names = {"-j", "--json"}, description = "Input JSON file containing the state machine definition.")
    private Path jsonFile;

    @Option(names = {"-o", "--output"}, description = "Output directory for the generated diagrams.", defaultValue = "./out")
    private Path outputDir;

    @Option(names = {"-f", "--format"}, description = "Comma-separated list of formats to export (dot, puml, scxml, json). If omitted, all formats are exported.", split = ",")
    private List<String> selectedFormats;

    @Option(names = {"--no-diamonds"}, description = "Render choice/junction states as regular states instead of diamonds.", negatable = true, defaultValue = "true")
    private boolean renderChoicesAsDiamonds;

    @Option(names = {"-p", "--profiles"}, description = "Spring profiles to activate for property resolution.", split = ",")
    private List<String> activeProfiles;

    @Option(names = {"--event"}, description = "Format for events when they are enums: fn (fullName, default), fqn (fully qualified name), sn (short name)", defaultValue = "fn")
    private click.kamil.springstatemachineexporter.exporter.EnumFormat eventFormat;

    @Option(names = {"--state"}, description = "Format for states when they are enums: fn (fullName, default), fqn (fully qualified name), sn (short name)", defaultValue = "fn")
    private click.kamil.springstatemachineexporter.exporter.EnumFormat stateFormat;

    @Option(names = {"--debug"}, description = "Enable diagnostic mode to show unresolved call chains and detailed analysis logging.", defaultValue = "false")
    private boolean debug;

    @Option(names = {"--include-generated-sources"}, description = "Scan existing Maven target/ and Gradle build/ trees for pre-generated .java sources (MapStruct, annotation processors, etc.). Does not run Maven or Gradle.", negatable = true, defaultValue = "true", fallbackValue = "true")
    private boolean includeGeneratedSources = true;

    @Option(names = {"--inline-accessors"}, description = "Index trivial JavaBean/record accessors at scan time and inline them during call-chain and constant resolution.", negatable = true, defaultValue = "true", fallbackValue = "true")
    private boolean inlineAccessors = true;

    @Override
    public Integer call() throws Exception {
        var out = spec.commandLine().getOut();
        var err = spec.commandLine().getErr();

        if (debug) {
            System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
            out.println(CommandLine.Help.Ansi.AUTO.string("@|bold,cyan Diagnostic mode enabled|@"));
        }

        if (inputDir == null && jsonFile == null) {
            inputDir = Path.of(".");
        }

        out.println(CommandLine.Help.Ansi.AUTO.string("@|bold,green Starting export process...|@"));
        if (jsonFile != null) {
            out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input JSON File:|@  " + jsonFile.toAbsolutePath()));
        } else {
            out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input Directory:|@  " + inputDir.toAbsolutePath()));
        }
        out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Output Directory:|@ " + outputDir.toAbsolutePath()));
        if (selectedFormats != null && !selectedFormats.isEmpty()) {
            out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Selected Formats:|@ " + String.join(", ", selectedFormats)));
        }

        try {
            List<String> profiles = activeProfiles != null ? activeProfiles : java.util.Collections.emptyList();
            if (jsonFile != null) {
                exportService.runJsonExporter(jsonFile, outputDir, selectedFormats, profiles, eventFormat, stateFormat, inputDir);
            } else {
                exportService.runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, profiles, null, null, eventFormat, stateFormat, includeGeneratedSources, inlineAccessors);
            }
            out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ All state machines have been exported to " + outputDir.toAbsolutePath()));
            return 0;
        } catch (Exception e) {
            err.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,red ERROR:|@ An error occurred during the export process: " + e.getMessage()));
            e.printStackTrace(err);
            return 1;
        }
    }
}
