using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Extensions.Logging;
using Moq;
using FluentAssertions;
using MaddoShared;

namespace MaddoShared.Tests
{
    [TestClass]
    public class ImageCreatorSharpTests
    {
        private ImageCreatorGDI CreateService(Action<PicSettings> customize = null)
        {
            var settings = new PicSettings
            {
                DimStandard = 20,
                DimStandardMiniatura = 10,
                LarghezzaSmall = 100,
                AltezzaSmall = 100,
                LarghezzaBig = 800,
                AltezzaBig = 600,
                Trasparenza = 50,
                IlFont = "Arial",
                Grassetto = false,
                Posizione = "CENTRO",
                Allineamento = "CENTRO",
                Margine = 10,
                MargVert = 10,
                TestoMin = false,
                AggNumTempMin = false
            };

            customize?.Invoke(settings);

            var logger = new Mock<ILogger<ImageCreatorGDI>>().Object;
            return new ImageCreatorGDI(settings, logger);
        }

        [TestMethod]
        public void CalculateThumbnailSize_Larghezza_UsesWidthScaling()
        {
            var svc = CreateService();
            var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var size = (Size)mi.Invoke(svc, new object[] { 400, 200, 200, "Larghezza" });

            size.Width.Should().Be(200);
            size.Height.Should().Be(100);
        }

        [TestMethod]
        public void CalculateThumbnailSize_Altezza_UsesHeightScaling()
        {
            var svc = CreateService();
            var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var size = (Size)mi.Invoke(svc, new object[] { 200, 400, 200, "Altezza" });

            size.Width.Should().Be(100);
            size.Height.Should().Be(200);
        }

        [TestMethod]
        public void IsSameDirectory_IsCaseInsensitive()
        {
            var svc = CreateService();
            var mi = svc.GetType().GetMethod("IsSameDirectory", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            bool same = (bool)mi.Invoke(svc, new object[] { @"C:\Temp", @"c:\temp" });
            same.Should().BeTrue();

            bool notSame = (bool)mi.Invoke(svc, new object[] { @"C:\TempA", @"c:\temp" });
            notSame.Should().BeFalse();
        }

        [TestMethod]
        public void UpdateFilenameWithCode_InsertsCodeBeforeExtension()
        {
            var svc = CreateService(s => s.Codice = "_X");
            var mi = svc.GetType().GetMethod("UpdateFilenameWithCode", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var state = new ImageState { NomeFileSmall = "photo123.jpg" };
            mi.Invoke(svc, new object[] { state });

            state.NomeFileSmall.Should().Be("photo123_X.jpg");
        }

        [DataTestMethod]
        [DataRow("SINISTRA")]
        [DataRow("CENTRO")]
        [DataRow("DESTRA")]
        public void CalculateHorizontalAlignment_RespectsAlignment(string alignment)
        {
            var svc = CreateService(s => { s.Allineamento = alignment; s.Margine = 20; });

            var mi = svc.GetType().GetMethod("CalculateHorizontalAlignment", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var center = (float)mi.Invoke(svc, new object[] { 800, 100f });

            if (alignment == "SINISTRA")
                center.Should().BeInRange(0f, 400f, "Expected left alignment range");
            if (alignment == "DESTRA")
                center.Should().BeInRange(400f, 800f, "Expected right alignment range");
            if (alignment == "CENTRO")
                center.Should().BeApproximately(800 / 2f, 0.0001f);
        }

        [TestMethod]
        public void SetVerticalPosition_AltoAndBasso_SetExpectedValues()
        {
            var svc = CreateService(s => s.Posizione = "ALTO");
            var mi = svc.GetType().GetMethod("SetVerticalPosition", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var state = new ImageState();

            // ALTO
            mi.Invoke(svc, new object[] { 500, 20f, state });
            state.YPosFromBottom1.Should().Be(10f);
            state.YPosFromBottom4.Should().Be(10f);

            // BASSO
            state = new ImageState();
            svc = CreateService(s => { s.Posizione = "BASSO"; s.Margine = 10; s.MargVert = 5; });
            mi = svc.GetType().GetMethod("SetVerticalPosition", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Invoke(svc, new object[] { 200, 20f, state });

            var expected1 = (float)(200 - 20 - (200 * 10 / 100.0));
            var expected4 = (float)(200 - 20 - (200 * 5 / 100.0));
            state.YPosFromBottom1.Should().BeApproximately(expected1, 0.001f);
            state.YPosFromBottom4.Should().BeApproximately(expected4, 0.001f);
        }

        [TestMethod]
        public void FormatTimeText_WithAndWithoutFileName_ProducesExpectedStrings()
        {
            var svc = CreateService();
            var mi = svc.GetType().GetMethod("FormatTimeText", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var state = new ImageState
            {
                NomeFileBig = "file.jpg",
                TestoOrario = "T:",
                DataPartenzaI = new DateTime(2024, 01, 01, 12, 0, 0),
                DataFoto = new DateTime(2024, 01, 01, 11, 59, 0)
            };
            var withoutName = (string)mi.Invoke(svc, new object[] { state, false });
            withoutName.Should().StartWith(Environment.NewLine);
            withoutName.Should().Contain("T:");

            var withName = (string)mi.Invoke(svc, new object[] { state, true });
            withName.Should().Contain("file.jpg");
            withName.Should().Contain("T:");
            withName.Should().Contain(Environment.NewLine);
        }

        [TestMethod]
        public void PrepareSignatureText_SetsSmallSignature_AccordingFlags()
        {
            var svc = CreateService();
            var miPrep = svc.GetType().GetMethod("PrepareSignatureText", BindingFlags.NonPublic | BindingFlags.Instance);
            miPrep.Should().NotBeNull();

            var state = new ImageState { NomeFileBig = "bigname.jpg" };

            svc = CreateService(s => s.TestoMin = true);
            miPrep.Invoke(svc, new object[] { state });
            state.TestoFirmaPiccola.Should().Be("bigname.jpg");

            state.TestoFirmaPiccola = "";
            svc = CreateService(s => { s.TestoMin = false; s.AggNumTempMin = true; });
            miPrep.Invoke(svc, new object[] { state });
            state.TestoFirmaPiccola.Should().Be("bigname.jpg ");
        }

        [TestMethod]
        public void ShouldRenderText_ReturnsCorrectFlag()
        {
            var svc = CreateService(s => { s.UsaOrarioMiniatura = false; s.TestoMin = false; s.AggTempoGaraMin = false; s.AggNumTempMin = false; });
            var mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance);
            mi.Should().NotBeNull();

            var res = (bool)mi.Invoke(svc, Array.Empty<object>());
            res.Should().BeFalse();

            svc = CreateService(s => s.TestoMin = true);
            mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance);
            res = (bool)mi.Invoke(svc, Array.Empty<object>());
            res.Should().BeTrue();
        }

        [TestMethod]
        public void FindBestFontSize_And_AdjustFontToFitWidth_ModifySizes()
        {
            var svc = CreateService(s => { s.IlFont = "Arial"; s.DimStandardMiniatura = 30; });

            using var bmp = new Bitmap(400, 100);
            using var g = Graphics.FromImage(bmp);

            var miFind = svc.GetType().GetMethod("FindBestFontSize", BindingFlags.NonPublic | BindingFlags.Instance);
            miFind.Should().NotBeNull();

            int best = (int)miFind.Invoke(svc, new object[] { g, "A very long text that won't fit", "Arial", 40, false, 50, 5 });
            best.Should().BeInRange(5, 40);

            var miAdjust = svc.GetType().GetMethod("AdjustFontToFitWidth", BindingFlags.NonPublic | BindingFlags.Instance);
            miAdjust.Should().NotBeNull();

            var imageState = new ImageState { DimensioneStandardMiniatura = 30, TestoFirmaPiccola = "A very long test string" };
            var initialFont = new Font("Arial", imageState.DimensioneStandardMiniatura);
            var textSize = g.MeasureString(imageState.TestoFirmaPiccola, initialFont);

            object[] parameters = new object[] { g, 50, imageState, textSize };
            miAdjust.Invoke(svc, parameters);

            var updatedSize = (SizeF)parameters[3];
            imageState.DimensioneStandardMiniatura.Should().BeLessThanOrEqualTo(30);
            (updatedSize.Width <= 50 || imageState.DimensioneStandardMiniatura <= 5).Should().BeTrue();
        }
    }
}
