package at.procon.ted.repair;

import at.procon.ted.config.TedProcessorProperties;
import at.procon.ted.model.entity.TedDailyPackage;
import at.procon.ted.repository.TedDailyPackageRepository;
import at.procon.ted.service.BatchDocumentProcessingService;
import at.procon.ted.service.TedPackageDownloadService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.data.domain.Sort;

import java.nio.file.Files;
import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class TedPackageRepairServiceTest {

    @TempDir
    Path tempDir;

    @Test
    void resolveCandidatesIncludesIncompletePackagesAndMissingSequenceGaps() {
        TedProcessorProperties properties = new TedProcessorProperties();
        properties.getRepair().setEnabled(true);
        properties.getRepair().setFromPackageIdentifier("202600001");
        properties.getRepair().setToPackageIdentifier("202600003");
        properties.getRepair().setIncludeMissingSequenceGaps(true);
        properties.getRepair().setMaxPackages(10);

        TedDailyPackageRepository repository = mock(TedDailyPackageRepository.class);
        TedDailyPackage pkg1 = newPackage("202600001", 2026, 1, TedDailyPackage.DownloadStatus.COMPLETED, 20, 20, 0);
        TedDailyPackage pkg3 = newPackage("202600003", 2026, 3, TedDailyPackage.DownloadStatus.PROCESSING, 20, 5, 0);
        when(repository.findAll(any(Sort.class))).thenReturn(List.of(pkg1, pkg3));

        TedPackageRepairService service = new TedPackageRepairService(
                properties,
                repository,
                mock(TedPackageDownloadService.class),
                mock(BatchDocumentProcessingService.class));

        List<TedPackageRepairService.RepairCandidate> candidates = service.resolveCandidates(properties.getRepair());

        assertThat(candidates).extracting(TedPackageRepairService.RepairCandidate::packageIdentifier)
                .containsExactly("202600002", "202600003");
        assertThat(candidates).extracting(TedPackageRepairService.RepairCandidate::reason)
                .containsExactly("MISSING_SEQUENCE_GAP", "STATUS_PROCESSING");
    }

    @Test
    void repairCandidateProcessesExistingArchiveAndMarksPackageCompleted() throws Exception {
        TedProcessorProperties properties = new TedProcessorProperties();
        properties.getRepair().setEnabled(true);
        properties.getRepair().setRedownloadMissingArchives(false);
        properties.getDownload().setDownloadDirectory(tempDir.toString());
        properties.getDownload().setDeleteAfterExtraction(false);

        Path archive = tempDir.resolve("202600003.tar.gz");
        Files.writeString(archive, "dummy");

        TedDailyPackageRepository repository = mock(TedDailyPackageRepository.class);
        TedDailyPackage pkg = newPackage("202600003", 2026, 3, TedDailyPackage.DownloadStatus.PROCESSING, 3, 0, 0);
        when(repository.save(any(TedDailyPackage.class))).thenAnswer(invocation -> invocation.getArgument(0));
        when(repository.findByPackageIdentifier("202600003")).thenReturn(Optional.of(pkg));

        TedPackageDownloadService downloadService = mock(TedPackageDownloadService.class);
        Path extractedDir = Files.createDirectory(tempDir.resolve("extracted"));
        Path xml1 = Files.writeString(extractedDir.resolve("a.xml"), "<a/>");
        Path xml2 = Files.writeString(extractedDir.resolve("b.xml"), "<b/>");
        Path xml3 = Files.writeString(extractedDir.resolve("c.xml"), "<c/>");
        when(downloadService.calculateArchiveHash(eq(archive))).thenReturn("hash-1");
        when(downloadService.extractArchive(eq(archive), eq("202600003"))).thenReturn(List.of(xml1, xml2, xml3));

        BatchDocumentProcessingService batchService = mock(BatchDocumentProcessingService.class);
        when(batchService.processBatch(any())).thenReturn(new BatchDocumentProcessingService.BatchProcessingResult(
                1, 2, 0, 5L, List.of(UUID.randomUUID()), List.of()));

        TedPackageRepairService service = new TedPackageRepairService(properties, repository, downloadService, batchService);
        TedPackageRepairService.RepairCandidate candidate = TedPackageRepairService.RepairCandidate.existing(pkg, "STATUS_PROCESSING");

        var result = service.repairCandidate(candidate, properties.getRepair());

        assertThat(result.outcome()).isEqualTo(TedPackageRepairService.RepairOutcome.COMPLETED);
        assertThat(pkg.getDownloadStatus()).isEqualTo(TedDailyPackage.DownloadStatus.COMPLETED);
        assertThat(pkg.getProcessedCount()).isEqualTo(3);
        assertThat(pkg.getFailedCount()).isZero();
        assertThat(pkg.getFileHash()).isEqualTo("hash-1");
        assertThat(pkg.getProcessedAt()).isNotNull();
    }

    private TedDailyPackage newPackage(String packageIdentifier,
                                    int year,
                                    int serial,
                                    TedDailyPackage.DownloadStatus status,
                                    Integer xmlCount,
                                    Integer processed,
                                    Integer failed) {
        TedDailyPackage pkg = new TedDailyPackage();
        pkg.setId(UUID.randomUUID());
        pkg.setPackageIdentifier(packageIdentifier);
        pkg.setYear(year);
        pkg.setSerialNumber(serial);
        pkg.setDownloadStatus(status);
        pkg.setXmlFileCount(xmlCount);
        pkg.setProcessedCount(processed);
        pkg.setFailedCount(failed);
        pkg.setDownloadUrl("https://ted.europa.eu/packages/daily/" + packageIdentifier);
        pkg.setCreatedAt(OffsetDateTime.now());
        pkg.setUpdatedAt(OffsetDateTime.now());
        return pkg;
    }
}
