package com.cameleer.server.app.retention;

import com.cameleer.server.core.runtime.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Nightly job that enforces JAR retention policies per environment.
 * For each app, keeps the N most recent versions (configured per environment)
 * and deletes older ones — unless they are currently deployed.
 */
@Component
public class JarRetentionJob {

    private static final Logger log = LoggerFactory.getLogger(JarRetentionJob.class);

    private final EnvironmentService environmentService;
    private final AppService appService;
    private final AppVersionRepository versionRepo;
    private final DeploymentRepository deploymentRepo;

    public JarRetentionJob(EnvironmentService environmentService,
                           AppService appService,
                           AppVersionRepository versionRepo,
                           DeploymentRepository deploymentRepo) {
        this.environmentService = environmentService;
        this.appService = appService;
        this.versionRepo = versionRepo;
        this.deploymentRepo = deploymentRepo;
    }

    @Scheduled(cron = "0 0 3 * * *") // 03:00 every day
    public void cleanupOldVersions() {
        log.info("JAR retention job started");
        int totalDeleted = 0;

        for (Environment env : environmentService.listAll()) {
            Integer retentionCount = env.jarRetentionCount();
            if (retentionCount == null) {
                log.debug("Environment {} has unlimited retention, skipping", env.slug());
                continue;
            }

            for (App app : appService.listByEnvironment(env.id())) {
                totalDeleted += cleanupApp(app, retentionCount);
            }
        }

        log.info("JAR retention job completed — deleted {} versions", totalDeleted);
    }

    private int cleanupApp(App app, int retentionCount) {
        List<AppVersion> versions = versionRepo.findByAppId(app.id()); // ordered DESC by version
        if (versions.size() <= retentionCount) return 0;

        // Find version IDs that are currently deployed (any status)
        Set<UUID> deployedVersionIds = deploymentRepo.findByAppId(app.id()).stream()
                .map(Deployment::appVersionId)
                .collect(Collectors.toSet());

        int deleted = 0;
        // versions is sorted DESC — skip the first retentionCount, delete the rest
        for (int i = retentionCount; i < versions.size(); i++) {
            AppVersion version = versions.get(i);
            if (deployedVersionIds.contains(version.id())) {
                log.debug("Skipping deployed version v{} of app {} ({})", version.version(), app.slug(), version.id());
                continue;
            }

            // Delete JAR from disk
            deleteJarFile(version);

            // Delete DB record
            versionRepo.delete(version.id());
            deleted++;
            log.info("Deleted version v{} of app {} ({}) — JAR: {}", version.version(), app.slug(), version.id(), version.jarPath());
        }

        return deleted;
    }

    private void deleteJarFile(AppVersion version) {
        try {
            Path jarPath = Path.of(version.jarPath());
            if (Files.exists(jarPath)) {
                Files.delete(jarPath);
                // Try to remove the empty version directory
                Path versionDir = jarPath.getParent();
                if (versionDir != null && Files.isDirectory(versionDir)) {
                    try (var entries = Files.list(versionDir)) {
                        if (entries.findFirst().isEmpty()) {
                            Files.delete(versionDir);
                        }
                    }
                }
            }
        } catch (IOException e) {
            log.warn("Failed to delete JAR file for version {}: {}", version.id(), e.getMessage());
        }
    }
}
