using System;
using System.Collections.Generic;
using CncModels.Dto;
using CncModels.Dto.Worker;
using CncService;
using CncWebApi.Controllers;
using Xunit;

namespace CncWebApi.Tests
{
    /// <summary>
    /// WorkerController单元测试
    /// 员工CRUD + 启停 + 绑定/解绑机床
    /// </summary>
    [Collection("Database")]
    public class WorkerControllerTests
    {
        private readonly WorkerController _controller;

        public WorkerControllerTests()
        {
            TestDb.TruncateAll();
            // 预置采集地址+机床（绑定/解绑测试需要）
            TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at)
                VALUES ('测试地址', 'http://192.168.1.1', 1, 5, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_machine (device_code, name, workshop_id, collect_address_id, ip_address, brand_id, is_enabled, created_at, updated_at)
                VALUES ('CNC001', '机床1', 1, 1, '192.168.1.100', 1, 1, NOW(), NOW())");
            _controller = ControllerFactory.CreateWorkerController();
        }

        /// <summary>
        /// 辅助：创建工人请求
        /// </summary>
        private CreateWorkerRequest CreateRequest(string name = "张三", string code = "W001") => new CreateWorkerRequest
        {
            Name = name,
            Code = code
        };

        #region GetList - 工人列表

        [Fact]
        public void GetList_EmptyDb_ShouldReturnEmpty()
        {
            var result = _controller.GetList(new WorkerQuery());
            var response = ControllerFactory.Extract<PagedResult<WorkerListItem>>(result);
            ControllerFactory.AssertSuccess(response);
            Assert.Empty(response.Data.Items);
        }

        [Fact]
        public void GetList_AfterCreate_ShouldReturnOne()
        {
            _controller.Create(CreateRequest());
            var result = _controller.GetList(new WorkerQuery());
            var response = ControllerFactory.Extract<PagedResult<WorkerListItem>>(result);
            Assert.Single(response.Data.Items);
        }

        #endregion

        #region GetById - 工人详情

        [Fact]
        public void GetById_Existing_ShouldReturnDetail()
        {
            _controller.Create(CreateRequest());
            int id = TestDb.QuerySingle<int>("SELECT MAX(id) FROM cnc_worker");

            var result = _controller.GetById(id);
            var response = ControllerFactory.Extract<WorkerDetailResponse>(result);
            ControllerFactory.AssertSuccess(response);
            Assert.Equal("张三", response.Data.Name);
            Assert.Equal("W001", response.Data.Code);
        }

        [Fact]
        public void GetById_NotExisting_ShouldThrow()
        {
            Assert.Throws<BusinessException>(() => _controller.GetById(999));
        }

        #endregion

        #region Create - 新增工人

        [Fact]
        public void Create_ValidRequest_ShouldReturnId()
        {
            var result = _controller.Create(CreateRequest());
            var response = ControllerFactory.Extract<object>(result);
            ControllerFactory.AssertSuccess(response);
        }

        [Fact]
        public void Create_DuplicateCode_ShouldThrow()
        {
            _controller.Create(CreateRequest("张三", "W001"));
            var ex = Assert.Throws<BusinessException>(() => _controller.Create(CreateRequest("李四", "W001")));
            Assert.Equal("工号已存在", ex.Message);
        }

        #endregion

        #region Update - 编辑工人

        [Fact]
        public void Update_ValidRequest_ShouldSuccess()
        {
            _controller.Create(CreateRequest());
            int id = TestDb.QuerySingle<int>("SELECT MAX(id) FROM cnc_worker");

            var result = _controller.Update(id, new UpdateWorkerRequest { Name = "张三改名" });
            ControllerFactory.AssertSuccess(ControllerFactory.Extract<object>(result));

            var detail = ControllerFactory.Extract<WorkerDetailResponse>(_controller.GetById(id));
            Assert.Equal("张三改名", detail.Data.Name);
        }

        #endregion

        #region Delete - 删除工人

        [Fact]
        public void Delete_Existing_ShouldSuccess()
        {
            _controller.Create(CreateRequest());
            int id = TestDb.QuerySingle<int>("SELECT MAX(id) FROM cnc_worker");

            var result = _controller.Delete(id);
            ControllerFactory.AssertSuccess(ControllerFactory.Extract<object>(result));
        }

        [Fact]
        public void Delete_NotExisting_ShouldNotThrow()
        {
            // Service层删除不存在ID时影响0行但不抛异常
            var result = _controller.Delete(999);
            Assert.NotNull(result);
        }

        #endregion

        #region ToggleEnabled - 启停

        [Fact]
        public void ToggleEnabled_ShouldSuccess()
        {
            _controller.Create(CreateRequest());
            int id = TestDb.QuerySingle<int>("SELECT MAX(id) FROM cnc_worker");

            var result = _controller.ToggleEnabled(id);
            ControllerFactory.AssertSuccess(ControllerFactory.Extract<object>(result));
        }

        #endregion

        #region BindMachine / UnbindMachine - 绑定/解绑

        [Fact]
        public void BindMachine_ShouldSuccess()
        {
            _controller.Create(CreateRequest());
            int workerId = TestDb.QuerySingle<int>("SELECT MAX(id) FROM cnc_worker");

            var result = _controller.BindMachine(workerId, new BindMachineRequest { MachineId = 1 });
            ControllerFactory.AssertSuccess(ControllerFactory.Extract<object>(result));
        }

        [Fact]
        public void UnbindMachine_ShouldSuccess()
        {
            // 先绑定
            _controller.Create(CreateRequest());
            int workerId = TestDb.QuerySingle<int>("SELECT MAX(id) FROM cnc_worker");
            _controller.BindMachine(workerId, new BindMachineRequest { MachineId = 1 });

            // 再解绑
            var result = _controller.UnbindMachine(workerId, new BindMachineRequest { MachineId = 1 });
            ControllerFactory.AssertSuccess(ControllerFactory.Extract<object>(result));
        }

        [Fact]
        public void BindMachine_NotExistingWorker_ShouldThrowDbException()
        {
            // 不存在的worker_id会触发外键约束，抛出MySqlException而非BusinessException
            Assert.ThrowsAny<Exception>(() =>
                _controller.BindMachine(999, new BindMachineRequest { MachineId = 1 }));
        }

        #endregion
    }
}
