package stirling.software.SPDF.config;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.context.annotation.Configuration;

import jakarta.annotation.PostConstruct;

import lombok.extern.slf4j.Slf4j;

import stirling.software.SPDF.config.EndpointConfiguration.DisableReason;
import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.common.util.RegexPatternUtils;

@Configuration
@Slf4j
public class ExternalAppDepConfig {

    private final EndpointConfiguration endpointConfiguration;

    private final String weasyprintPath;
    private final String unoconvPath;
    private final Map<String, List<String>> commandToGroupMapping;
    private volatile boolean dependenciesChecked = false;

    public ExternalAppDepConfig(
            EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) {
        this.endpointConfiguration = endpointConfiguration;
        weasyprintPath = runtimePathConfig.getWeasyPrintPath();
        unoconvPath = runtimePathConfig.getUnoConvertPath();

        commandToGroupMapping =
                new HashMap<>() {

                    {
                        put("gs", List.of("Ghostscript"));
                        put("ocrmypdf", List.of("OCRmyPDF"));
                        put("soffice", List.of("LibreOffice"));
                        put(weasyprintPath, List.of("Weasyprint"));
                        put("pdftohtml", List.of("Pdftohtml"));
                        put(unoconvPath, List.of("Unoconvert"));
                        put("qpdf", List.of("qpdf"));
                        put("tesseract", List.of("tesseract"));
                        put("rar", List.of("rar")); // Required for real CBR output
                    }
                };
    }

    private boolean isCommandAvailable(String command) {
        try {
            ProcessBuilder processBuilder = new ProcessBuilder();
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                processBuilder.command("where", command);
            } else {
                processBuilder.command("which", command);
            }
            Process process = processBuilder.start();
            int exitCode = process.waitFor();
            return exitCode == 0;
        } catch (Exception e) {
            log.debug("Error checking for command {}: {}", command, e.getMessage());
            return false;
        }
    }

    private List<String> getAffectedFeatures(String group) {
        return endpointConfiguration.getEndpointsForGroup(group).stream()
                .map(endpoint -> formatEndpointAsFeature(endpoint))
                .toList();
    }

    private String formatEndpointAsFeature(String endpoint) {
        // First replace common terms
        String feature = endpoint.replace("-", " ").replace("pdf", "PDF").replace("img", "image");
        // Split into words and capitalize each word
        return Arrays.stream(RegexPatternUtils.getInstance().getWordSplitPattern().split(feature))
                .map(word -> capitalizeWord(word))
                .collect(Collectors.joining(" "));
    }

    private String capitalizeWord(String word) {
        if (word.isEmpty()) {
            return word;
        }
        if ("pdf".equalsIgnoreCase(word)) {
            return "PDF";
        }
        return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
    }

    private void checkDependencyAndDisableGroup(String command) {
        boolean isAvailable = isCommandAvailable(command);
        if (!isAvailable) {
            List<String> affectedGroups = commandToGroupMapping.get(command);
            if (affectedGroups != null) {
                for (String group : affectedGroups) {
                    List<String> affectedFeatures = getAffectedFeatures(group);
                    endpointConfiguration.disableGroup(group, DisableReason.DEPENDENCY);
                    log.warn(
                            "Missing dependency: {} - Disabling group: {} (Affected features: {})",
                            command,
                            group,
                            !affectedFeatures.isEmpty()
                                    ? String.join(", ", affectedFeatures)
                                    : "unknown");
                }
            }
        }
    }

    public boolean isDependenciesChecked() {
        return dependenciesChecked;
    }

    @PostConstruct
    public void checkDependencies() {
        // Check core dependencies
        checkDependencyAndDisableGroup("gs");
        checkDependencyAndDisableGroup("ocrmypdf");
        checkDependencyAndDisableGroup("tesseract");
        checkDependencyAndDisableGroup("soffice");
        checkDependencyAndDisableGroup("qpdf");
        checkDependencyAndDisableGroup(weasyprintPath);
        checkDependencyAndDisableGroup("pdftohtml");
        checkDependencyAndDisableGroup(unoconvPath);
        checkDependencyAndDisableGroup("rar");
        // Special handling for Python/OpenCV dependencies
        boolean pythonAvailable = isCommandAvailable("python3") || isCommandAvailable("python");
        if (!pythonAvailable) {
            List<String> pythonFeatures = getAffectedFeatures("Python");
            List<String> openCVFeatures = getAffectedFeatures("OpenCV");
            endpointConfiguration.disableGroup("Python", DisableReason.DEPENDENCY);
            endpointConfiguration.disableGroup("OpenCV", DisableReason.DEPENDENCY);
            log.warn(
                    "Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}",
                    String.join(", ", pythonFeatures),
                    String.join(", ", openCVFeatures));
        } else {
            // If Python is available, check for OpenCV
            try {
                ProcessBuilder processBuilder = new ProcessBuilder();
                if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                    processBuilder.command("python", "-c", "import cv2");
                } else {
                    processBuilder.command("python3", "-c", "import cv2");
                }
                Process process = processBuilder.start();
                int exitCode = process.waitFor();
                if (exitCode != 0) {
                    List<String> openCVFeatures = getAffectedFeatures("OpenCV");
                    endpointConfiguration.disableGroup("OpenCV", DisableReason.DEPENDENCY);
                    log.warn(
                            "OpenCV not available in Python - Disabling OpenCV features: {}",
                            String.join(", ", openCVFeatures));
                }
            } catch (Exception e) {
                List<String> openCVFeatures = getAffectedFeatures("OpenCV");
                endpointConfiguration.disableGroup("OpenCV", DisableReason.DEPENDENCY);
                log.warn(
                        "Error checking OpenCV: {} - Disabling OpenCV features: {}",
                        e.getMessage(),
                        String.join(", ", openCVFeatures));
            }
        }
        endpointConfiguration.logDisabledEndpointsSummary();
        dependenciesChecked = true;
        log.info("Dependency checks completed");
    }
}
