using System;
using NUnit.Framework;
using NcProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Fagor;
using NcProgramManager.Tests.Integration.Stubs;

namespace NcProgramManager.Tests.Integration
{
    [TestFixture]
    [Category("Integration")]
    public class FagorMachineIntegrationTests
    {
        private FtpServerStub _stub;
        private FagorMachine _machine;
        private const string ProgramDir = "/Users/Prg/";

        [SetUp]
        public void SetUp()
        {
            _stub = new FtpServerStub();
            var config = new FagorConnectionConfig
            {
                Name = "FagorIntegration",
                IpAddress = "127.0.0.1",
                Port = _stub.Port,
                Username = "user",
                Password = "pass",
                ProgramDirectory = ProgramDir,
                ProgramExtension = ".nc",
                RequestTimeout = TimeSpan.FromSeconds(5)
            };
            _machine = new FagorMachine(config);
        }

        [TearDown]
        public void TearDown()
        {
            _machine?.Dispose();
            _stub?.Dispose();
        }

        [Test]
        public void ConnectAsync_StubListens_ResultOk()
        {
            var result = _machine.ConnectAsync().GetAwaiter().GetResult();

            Assert.That(result.Success, Is.True);
            Assert.That(_machine.State, Is.EqualTo(ConnectionState.Connected));
        }

        [Test]
        public void WriteThenRead_ProgramRoundTrips()
        {
            _machine.ConnectAsync().GetAwaiter().GetResult();
            string content = "G0 X0 Y0\nM30";
            var prog = new CncProgram { Name = "PROG1", Content = content };

            var writeResult = _machine.WriteProgramAsync(ProgramDir, prog).GetAwaiter().GetResult();
            Assert.That(writeResult.Success, Is.True);

            // ProgramExtension ".nc" applied by FagorMachine
            string storedPath = ProgramDir + "PROG1.nc";
            var readResult = _machine.ReadProgramAsync(storedPath).GetAwaiter().GetResult();

            Assert.That(readResult.Success, Is.True);
            Assert.That(readResult.Value, Is.EqualTo(content));
        }

        [Test]
        public void DeleteProgramAsync_FileExistsInStub_RemovedFromStub()
        {
            _machine.ConnectAsync().GetAwaiter().GetResult();
            string path = ProgramDir + "PROG2.nc";
            _stub.Files[path] = "SOME_CONTENT";

            var result = _machine.DeleteProgramAsync(path).GetAwaiter().GetResult();

            Assert.That(result.Success, Is.True);
            Assert.That(_stub.Files.ContainsKey(path), Is.False);
        }

        [Test]
        public void ReadProgramAsync_FileNotInStub_ResultFail()
        {
            _machine.ConnectAsync().GetAwaiter().GetResult();

            var result = _machine.ReadProgramAsync(ProgramDir + "DOESNOTEXIST.nc").GetAwaiter().GetResult();

            Assert.That(result.Success, Is.False);
        }

        [Test]
        public void WriteProgramAsync_InvalidName_ValidationFails()
        {
            _machine.ConnectAsync().GetAwaiter().GetResult();
            // Fagor name max 24 chars — 25 'A' triggers validator failure
            var prog = new CncProgram { Name = new string('A', 25), Content = "G00 X10\nM30" };

            var result = _machine.WriteProgramAsync(ProgramDir, prog).GetAwaiter().GetResult();

            Assert.That(result.Success, Is.False);
        }

        [Test]
        [Category("Hardware")]
        public void Hardware_ConnectAsync_RealMachine()
        {
            string ip = HardwareTestHelper.GetIpOrSkip("FAGOR_TEST_IP");
            var config = new FagorConnectionConfig
            {
                Name             = "FagorHardware",
                IpAddress        = ip,
                Port             = 21,
                Username         = Environment.GetEnvironmentVariable("FAGOR_USER") ?? "user",
                Password         = Environment.GetEnvironmentVariable("FAGOR_PASS") ?? "pass",
                ProgramDirectory = "/Users/Prg/",
                ProgramExtension = ".nc",
                RequestTimeout   = TimeSpan.FromSeconds(10)
            };
            using (var machine = new FagorMachine(config))
            {
                var result = machine.ConnectAsync().GetAwaiter().GetResult();
                Assert.That(result.Success, Is.True, "ConnectAsync failed: " + string.Join("; ", result.Errors));
            }
        }
    }
}
