package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.doReturn;

import dev.lions.unionflow.server.api.dto.backup.request.RestoreBackupRequest;
import dev.lions.unionflow.server.api.dto.backup.response.BackupResponse;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectSpy;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests pour les chemins nominaux de {@link BackupService#restoreBackup} et
 * {@link BackupService#deleteBackup}.
 *
 * <p>{@link BackupServiceTest} ne peut pas couvrir les lignes 153-179 de {@code restoreBackup}
 * car {@code getAllBackups()} génère de nouveaux {@link UUID} à chaque appel — l'ID passé en
 * paramètre ne matche donc jamais. Ce test utilise {@code @InjectSpy} pour que
 * {@code getAllBackups()} retourne une liste fixe avec un UUID stable.
 */
@QuarkusTest
class BackupServiceRestoreSuccessTest {

    @InjectSpy
    BackupService backupService;

    @InjectMock
    SecurityIdentity securityIdentity;

    private static final UUID FIXED_ID = UUID.fromString("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee");

    private BackupResponse completedBackup;
    private BackupResponse inProgressBackup;

    @BeforeEach
    void setUp() {
        completedBackup = BackupResponse.builder()
                .id(FIXED_ID)
                .name("Sauvegarde stable")
                .description("Test stable")
                .type("AUTO")
                .sizeBytes(1_000_000L)
                .sizeFormatted("1 MB")
                .status("COMPLETED")
                .createdAt(LocalDateTime.now().minusHours(1))
                .completedAt(LocalDateTime.now().minusMinutes(30))
                .createdBy("test-user")
                .includesDatabase(true)
                .includesFiles(true)
                .includesConfiguration(true)
                .filePath("/backups/stable-test.zip")
                .build();

        inProgressBackup = BackupResponse.builder()
                .id(FIXED_ID)
                .name("Sauvegarde en cours")
                .description("Test non complété")
                .type("AUTO")
                .sizeBytes(500_000L)
                .sizeFormatted("500 KB")
                .status("IN_PROGRESS")
                .createdAt(LocalDateTime.now().minusMinutes(10))
                .createdBy("test-user")
                .includesDatabase(true)
                .includesFiles(true)
                .includesConfiguration(true)
                .filePath("/backups/in-progress-test.zip")
                .build();

        // Sécurité : getPrincipal() ne doit pas être nul si invoqué
        java.security.Principal principal = () -> "test-user";
        org.mockito.Mockito.when(securityIdentity.getPrincipal()).thenReturn(principal);
    }

    // =========================================================================
    // restoreBackup — chemin nominal COMPLETED sans createRestorePoint
    // Couvre lignes 153-179 (20 lignes manquantes)
    // =========================================================================

    @Test
    @DisplayName("restoreBackup avec backup COMPLETED et createRestorePoint=false — chemin nominal sans exception")
    void restoreBackup_completedBackup_noRestorePoint_success() {
        // getAllBackups() est stubé → getBackupById(FIXED_ID) trouve le backup
        doReturn(List.of(completedBackup)).when(backupService).getAllBackups();

        RestoreBackupRequest request = RestoreBackupRequest.builder()
                .backupId(FIXED_ID)
                .createRestorePoint(false)
                .restoreDatabase(true)
                .restoreFiles(true)
                .restoreConfiguration(true)
                .build();

        // Ne doit pas lancer d'exception — couvre lignes 153, 158, 173-179
        assertThatCode(() -> backupService.restoreBackup(request)).doesNotThrowAnyException();
    }

    @Test
    @DisplayName("restoreBackup avec backup COMPLETED et createRestorePoint=true — crée un restore point")
    void restoreBackup_completedBackup_withRestorePoint_success() {
        doReturn(List.of(completedBackup)).when(backupService).getAllBackups();

        RestoreBackupRequest request = RestoreBackupRequest.builder()
                .backupId(FIXED_ID)
                .createRestorePoint(true)   // → branche true : createBackup() appelé
                .restoreDatabase(true)
                .restoreFiles(false)
                .restoreConfiguration(true)
                .build();

        // Couvre ligne 158 (true branch) → 159-168 (creation restore point)
        assertThatCode(() -> backupService.restoreBackup(request)).doesNotThrowAnyException();
    }

    // =========================================================================
    // deleteBackup — chemin nominal (lignes 191-195)
    // =========================================================================

    @Test
    @DisplayName("deleteBackup avec ID valide (stub getAllBackups) — chemin nominal sans exception")
    void deleteBackup_validId_success() {
        doReturn(List.of(completedBackup)).when(backupService).getAllBackups();

        // Couvre lignes 192-195 : log filePath, log succès
        assertThatCode(() -> backupService.deleteBackup(FIXED_ID)).doesNotThrowAnyException();
    }

    // =========================================================================
    // getBackupById — lambda orElseThrow (ligne 107)
    // =========================================================================

    @Test
    @DisplayName("getBackupById avec ID valide retourne le backup correct")
    void getBackupById_validId_returnsBackup() {
        doReturn(List.of(completedBackup)).when(backupService).getAllBackups();

        BackupResponse result = backupService.getBackupById(FIXED_ID);

        org.assertj.core.api.Assertions.assertThat(result).isNotNull();
        org.assertj.core.api.Assertions.assertThat(result.getId()).isEqualTo(FIXED_ID);
        org.assertj.core.api.Assertions.assertThat(result.getName()).isEqualTo("Sauvegarde stable");
    }
}
