package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThat;

import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * Tests complémentaires pour {@link FileStorageService#storeFile} — vérification que
 * MD5 et SHA-256 sont bien calculés et que le bloc catch sur MessageDigest n'est pas atteint.
 *
 * <p>Note : le bloc catch autour de {@code MessageDigest.getInstance} est du code défensif mort
 * car MD5 et SHA-256 sont des algorithmes obligatoires depuis Java 1.4 (standard JCA).
 */
@QuarkusTest
@DisplayName("FileStorageService — calcul des hash MD5 et SHA-256")
class FileStorageServiceBranchesMissingTest {

    @Inject
    FileStorageService fileStorageService;

    @Nested
    @DisplayName("catch(Exception) MessageDigest — branche inaccessible dans un JRE standard")
    class MessageDigestCatchBranchTest {

        @Test
        @DisplayName("MD5 et SHA-256 disponibles → hashMd5 et hashSha256 non-null")
        void storeFile_messageDigestDisponibles_catchL90NonAtteint() throws Exception {
            byte[] content = "contenu test L90".getBytes();

            FileStorageService.FileMetadata meta = fileStorageService.storeFile(
                    new ByteArrayInputStream(content),
                    "test-l90.pdf",
                    "application/pdf",
                    content.length);

            // Algorithmes disponibles → hash calculés correctement
            assertThat(meta.getHashMd5()).isNotNull().hasSize(32);
            assertThat(meta.getHashSha256()).isNotNull().hasSize(64);
        }

        @Test
        @DisplayName("Avec contenu image JPEG → hash générés")
        void storeFile_avecImageJpeg_hashGeneres_catchNonAtteint() throws Exception {
            byte[] content = "fake jpeg content for L90 test".getBytes();

            FileStorageService.FileMetadata meta = fileStorageService.storeFile(
                    new ByteArrayInputStream(content),
                    "photo-l90.jpg",
                    "image/jpeg",
                    content.length);

            assertThat(meta.getHashMd5()).isNotNull();
            assertThat(meta.getHashSha256()).isNotNull();
        }
    }

    // =========================================================================
    // L114-115 : ternaires md5/sha256 != null dans storeFile
    //
    // Code source :
    //   String hashMd5 = md5 != null ? bytesToHex(md5.digest()) : null;         // L114
    //   String hashSha256 = sha256 != null ? bytesToHex(sha256.digest()) : null; // L115
    //
    // Branche true (md5/sha256 non null → bytesToHex(...)) : couverte par les tests existants
    //   et par les tests ci-dessus.
    // Branche false (md5/sha256 null → null) : inatteignable sans catch L90-91.
    //
    // Ces tests couvrent explicitement la branche true des ternaires L114-115 avec différents
    // types de fichiers et différentes tailles de contenu.
    // =========================================================================

    @Nested
    @DisplayName("L114-115 — ternaires md5/sha256 != null (branche true, branche false inatteignable)")
    class HashTernairesBranchTest {

        @Test
        @DisplayName("contenu multi-chunk → md5 et sha256 non null → hash hex retournés")
        void storeFile_md5NonNull_brancheTrueL114_hashHexRetourne() throws Exception {
            // Contenu multi-chunk (>8192 bytes) pour tester plusieurs passages dans la boucle
            byte[] content = new byte[10000];
            for (int i = 0; i < content.length; i++) {
                content[i] = (byte) (i % 256);
            }

            FileStorageService.FileMetadata meta = fileStorageService.storeFile(
                    new ByteArrayInputStream(content),
                    "grand-fichier-l114.pdf",
                    "application/pdf",
                    content.length);

            assertThat(meta.getHashMd5()).isNotNull().hasSize(32).matches("[0-9a-f]+");
            assertThat(meta.getHashSha256()).isNotNull().hasSize(64).matches("[0-9a-f]+");
        }

        @Test
        @DisplayName("contenu minimal (1 byte) → md5 et sha256 non null → hash retournés")
        void storeFile_contenu1Byte_md5EtSha256NonNull_hashRetournes() throws Exception {
            byte[] content = new byte[]{42};

            FileStorageService.FileMetadata meta = fileStorageService.storeFile(
                    new ByteArrayInputStream(content),
                    "tiny-l115.png",
                    "image/png",
                    content.length);

            // Les deux hash sont non-null → branches true de L114 et L115
            assertThat(meta.getHashMd5()).isNotNull();
            assertThat(meta.getHashSha256()).isNotNull();

            // Vérification de cohérence : même contenu → même hash
            FileStorageService.FileMetadata meta2 = fileStorageService.storeFile(
                    new ByteArrayInputStream(content),
                    "tiny-l115-copy.png",
                    "image/png",
                    content.length);

            assertThat(meta.getHashMd5()).isEqualTo(meta2.getHashMd5());
            assertThat(meta.getHashSha256()).isEqualTo(meta2.getHashSha256());
        }

        @Test
        @DisplayName("contenu 2 chunks (8193 bytes) → hash calculés sur tous les chunks")
        void storeFile_contenu2Chunks_md5EtSha256UpdateAppeles_brancheTrueL104L107() throws Exception {
            // 8193 bytes → 2 passages dans la boucle (buffer de 8192 bytes)
            byte[] content = new byte[8193];
            for (int i = 0; i < content.length; i++) {
                content[i] = (byte) (i % 128);
            }

            FileStorageService.FileMetadata meta = fileStorageService.storeFile(
                    new ByteArrayInputStream(content),
                    "two-chunks-l104.pdf",
                    "application/pdf",
                    content.length);

            // Hash valides même avec plusieurs chunks de lecture
            assertThat(meta.getHashMd5()).isNotNull().hasSize(32);
            assertThat(meta.getHashSha256()).isNotNull().hasSize(64);
            assertThat(meta.getTailleOctets()).isEqualTo(8193);
        }
    }
}
