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

namespace NcProgramManager.Tests.Integration
{
    [TestFixture]
    [Category("Integration")]
    public class MitsubishiMachineIntegrationTests
    {
        private FtpServerStub _stub;
        private MitsubishiMachine _machine;
        private const string ProgramDir = "/PRG/";

        [SetUp]
        public void SetUp()
        {
            _stub = new FtpServerStub();
            var config = new MitsubishiConnectionConfig
            {
                Name = "MitsubishiIntegration",
                IpAddress = "127.0.0.1",
                Port = _stub.Port,
                Username = "user",
                Password = "pass",
                ProgramDirectory = ProgramDir,
                RequestTimeout = TimeSpan.FromSeconds(5)
            };
            _machine = new MitsubishiMachine(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_ProgramRoundTripsNoExtension()
        {
            _machine.ConnectAsync().GetAwaiter().GetResult();
            // Mitsubishi: name O\d{4}; first line must be O####; no semicolons/brackets; no lowercase
            string content = "O1234\nG0 X0 Y0\nM30";
            var prog = new CncProgram { Name = "O1234", Content = content };

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

            // Mitsubishi: no extension
            string storedPath = ProgramDir + "O1234";
            Assert.That(_stub.Files.ContainsKey(storedPath), Is.True,
                "File should be stored without extension at " + storedPath);
            Assert.That(_stub.Files.ContainsKey(storedPath + ".MPF"), Is.False,
                "File must NOT be stored with .MPF extension");

            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 + "O5678";
            _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 + "O9999").GetAwaiter().GetResult();

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

        [Test]
        [Category("Hardware")]
        public void Hardware_ConnectAsync_RealMachine()
        {
            string ip = HardwareTestHelper.GetIpOrSkip("MITSUBISHI_IP");
            var config = new MitsubishiConnectionConfig
            {
                Name             = "MitsubishiHardware",
                IpAddress        = ip,
                Port             = 21,
                Username         = Environment.GetEnvironmentVariable("MITSUBISHI_USER") ?? "user",
                Password         = Environment.GetEnvironmentVariable("MITSUBISHI_PASS") ?? "pass",
                ProgramDirectory = "/PRG/",
                RequestTimeout   = TimeSpan.FromSeconds(10)
            };
            using (var machine = new MitsubishiMachine(config))
            {
                var result = machine.ConnectAsync().GetAwaiter().GetResult();
                Assert.That(result.Success, Is.True, "ConnectAsync failed: " + string.Join("; ", result.Errors));
            }
        }
    }
}
