using MaBibli.Client.Scan;
using ZXing.Common;
using ZXing.OneD;

namespace MaBibli.Tests;

/// <summary>
/// Tests du décodage pur (aucune caméra, aucun navigateur) : on encode un EAN-13 avec
/// ZXing, on le convertit en buffer RGBA comme le ferait <c>getImageData</c>, et on
/// vérifie le trajet aller-retour. Aucune image externe n'est nécessaire.
/// </summary>
public class IsbnScannerTests
{
    // Les ISBN de vérification du projet (voir CLAUDE.md).
    [Theory]
    [InlineData("9782070612758")]  // Le Petit Prince
    [InlineData("9782253004226")]  // Germinal
    [InlineData("9782080704092")]  // Le Horla
    [InlineData("9780262033848")]  // Introduction to Algorithms
    public void Decode_un_EAN13_genere(string isbn)
    {
        var (rgba, largeur, hauteur) = ImageEan13(isbn);

        Assert.Equal(isbn, IsbnScanner.TryDecode(rgba, largeur, hauteur));
    }

    [Fact]
    public void Decode_aussi_un_EAN8()
    {
        // Pas un ISBN, mais le format est dans les hints : le décodeur doit le lire,
        // c'est la couche appelante qui écarte les codes non-ISBN.
        var matrice = new EAN8Writer().encode("96385074", ZXing.BarcodeFormat.EAN_8, 260, 160);
        var (rgba, largeur, hauteur) = VersRgba(matrice);

        Assert.Equal("96385074", IsbnScanner.TryDecode(rgba, largeur, hauteur));
    }

    [Fact]
    public void Renvoie_null_sur_une_frame_sans_code_barres()
    {
        // Cas très majoritaire en usage réel : la caméra filme autre chose.
        // ZXing doit rendre null, jamais lever d'exception.
        const int largeur = 640, hauteur = 480;
        var rgba = new byte[largeur * hauteur * 4];
        var alea = new Random(1234);
        alea.NextBytes(rgba);

        Assert.Null(IsbnScanner.TryDecode(rgba, largeur, hauteur));
    }

    [Fact]
    public void Renvoie_null_sur_une_image_uniforme()
    {
        var rgba = new byte[320 * 200 * 4];
        Array.Fill(rgba, (byte)255);

        Assert.Null(IsbnScanner.TryDecode(rgba, 320, 200));
    }

    [Theory]
    [InlineData(0, 100)]
    [InlineData(100, 0)]
    [InlineData(-5, 100)]
    public void Renvoie_null_sur_des_dimensions_absurdes(int largeur, int hauteur)
        => Assert.Null(IsbnScanner.TryDecode(new byte[4000], largeur, hauteur));

    [Fact]
    public void Renvoie_null_si_le_buffer_est_trop_court()
    {
        // Une frame tronquée ne doit pas faire tomber la boucle de scan.
        Assert.Null(IsbnScanner.TryDecode(new byte[100], 640, 480));
    }

    private static (byte[] Rgba, int Largeur, int Hauteur) ImageEan13(string isbn)
        => VersRgba(new EAN13Writer().encode(isbn, ZXing.BarcodeFormat.EAN_13, 380, 160));

    /// <summary>
    /// Convertit une BitMatrix en buffer RGBA, exactement la forme que le canvas
    /// du navigateur fournit à C# (4 octets par pixel, opaque).
    /// </summary>
    private static (byte[] Rgba, int Largeur, int Hauteur) VersRgba(BitMatrix matrice)
    {
        var largeur = matrice.Width;
        var hauteur = matrice.Height;
        var rgba = new byte[largeur * hauteur * 4];

        for (var y = 0; y < hauteur; y++)
        {
            for (var x = 0; x < largeur; x++)
            {
                var valeur = matrice[x, y] ? (byte)0 : (byte)255;
                var i = ((y * largeur) + x) * 4;
                rgba[i] = valeur;
                rgba[i + 1] = valeur;
                rgba[i + 2] = valeur;
                rgba[i + 3] = 255;
            }
        }

        return (rgba, largeur, hauteur);
    }
}
