using System;
using CncModels.Constants;
using CncModels.Dto;
using CncModels.Dto.Production;
using CncService;
using CncService.Impl;
using Xunit;

namespace CncService.Tests
{
    /// <summary>
    /// ProductionService 产量管理测试
    /// 测试场景：查询、日汇总、日期范围总产量、产量修正、参数校验
    /// </summary>
    [Collection("Database")]
    public class ProductionServiceTests : IDisposable
    {
        private readonly ProductionService _service;

        public ProductionServiceTests()
        {
            TestDb.TruncateAll();
            _service = ServiceFactory.CreateProductionService();
        }

        public void Dispose()
        {
            TestDb.TruncateAll();
        }

        // ======== GetList ========

        [Fact]
        public void GetList_无数据_返回空列表()
        {
            var result = _service.GetList(new ProductionQuery { Page = 1, PageSize = 20 });
            Assert.NotNull(result);
            Assert.Equal(0, result.Total);
        }

        [Fact]
        public void GetList_查询参数为null_抛出BadRequest异常()
        {
            var ex = Assert.Throws<BusinessException>(() => _service.GetList(null));
            Assert.Equal(ErrorCode.BadRequest, ex.Code);
        }

        [Fact]
        public void GetList_有产量数据_返回分页结果()
        {
            // 插入机床+日产量数据
            TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at)
                VALUES ('测试地址', 'http://test', 1, 30, 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 ('M001', '机床1', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_daily_production (machine_id, production_date, program_name, total_quantity, created_at, updated_at)
                VALUES (1, CURDATE(), 'O0001', 100, NOW(), NOW())");

            var result = _service.GetList(new ProductionQuery { Page = 1, PageSize = 20 });
            Assert.Equal(1, result.Total);
        }

        // ======== GetSummary ========

        [Fact]
        public void GetSummary_今日无产量_返回0()
        {
            var summary = _service.GetSummary(null, null, null, null, null);
            Assert.NotNull(summary);
            Assert.Equal(0, summary.TotalQuantity);
        }

        [Fact]
        public void GetSummary_指定日期有产量_返回正确数量()
        {
            TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at)
                VALUES ('测试地址', 'http://test', 1, 30, 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 ('M001', '机床1', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_daily_production (machine_id, production_date, program_name, total_quantity, created_at, updated_at)
                VALUES (1, CURDATE(), 'O0001', 150, NOW(), NOW())");

            var summary = _service.GetSummary(DateTime.Today, null, null, null, null);
            Assert.Equal(150, summary.TotalQuantity);
        }

        // ======== GetTotalByDateRange ========

        [Fact]
        public void GetTotalByDateRange_范围内无数据_返回0()
        {
            var total = _service.GetTotalByDateRange(
                new DateTime(2020, 1, 1),
                new DateTime(2020, 1, 31),
                null);
            Assert.Equal(0m, total);
        }

        // ======== Adjust ========

        [Fact]
        public void Adjust_正常修正_返回true()
        {
            var result = _service.Adjust(new ProductionAdjustRequest
            {
                TargetTable = "cnc_daily_production",
                TargetId = 1,
                FieldName = "total_quantity",
                NewValue = "200",
                Reason = "数据修正测试"
            });
            Assert.True(result);
        }

        [Fact]
        public void Adjust_请求为null_抛出BadRequest异常()
        {
            var ex = Assert.Throws<BusinessException>(() => _service.Adjust(null));
            Assert.Equal(ErrorCode.BadRequest, ex.Code);
        }

        [Fact]
        public void Adjust_修正记录已插入数据库()
        {
            _service.Adjust(new ProductionAdjustRequest
            {
                TargetTable = "cnc_daily_production",
                TargetId = 1,
                FieldName = "total_quantity",
                NewValue = "200",
                Reason = "测试原因"
            });

            var count = TestDb.QuerySingle<int>("SELECT COUNT(*) FROM cnc_production_adjustment");
            Assert.Equal(1, count);
        }

        // ======== GetMachineSummary ========

        [Fact]
        public void GetMachineSummary_无数据_返回0值()
        {
            var result = _service.GetMachineSummary(null, null, null);
            Assert.NotNull(result);
            Assert.Equal(0, result.TotalQuantity);
            Assert.Equal(0, result.RunningMachineCount);
        }

        [Fact]
        public void GetMachineSummary_有数据_返回正确统计()
        {
            // 插入测试数据：2台机床，各生产一个程序
            TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at)
                VALUES ('测试地址', 'http://test', 1, 30, 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 ('M001', '机床1', 1, 1, '0.0.0.0', 1, 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 ('M002', '机床2', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at)
                VALUES (1, CURDATE(), 'O0001', NOW(), 0, 100, 100, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at)
                VALUES (2, CURDATE(), 'O0002', NOW(), 0, 50, 50, 1, NOW(), NOW())");

            var result = _service.GetMachineSummary(DateTime.Today, DateTime.Today, null);
            Assert.Equal(150, result.TotalQuantity);
            Assert.Equal(2, result.RunningMachineCount);
            Assert.Equal(75m, result.AvgPerMachine);
            Assert.NotEmpty(result.TopMachineName);
        }

        // ======== GetMachineList ========

        [Fact]
        public void GetMachineList_无数据_返回空列表()
        {
            var result = _service.GetMachineList(null, null, null, null);
            Assert.NotNull(result);
            Assert.Empty(result);
        }

        [Fact]
        public void GetMachineList_有数据_返回排行()
        {
            TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at)
                VALUES ('测试地址', 'http://test', 1, 30, 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 ('M001', '机床1', 1, 1, '0.0.0.0', 1, 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 ('M002', '机床2', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at)
                VALUES (1, CURDATE(), 'O0001', NOW(), 0, 100, 100, 1, NOW(), NOW())");
            TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at)
                VALUES (2, CURDATE(), 'O0002', NOW(), 0, 50, 50, 1, NOW(), NOW())");

            var result = _service.GetMachineList(DateTime.Today, DateTime.Today, null, null);
            Assert.NotEmpty(result);
            Assert.Equal(2, result.Count);
            Assert.Equal(1, result[0].Rank);
            Assert.Equal("机床1", result[0].MachineName);
            Assert.Equal(100, result[0].TotalQuantity);
            Assert.Equal(2, result[1].Rank);
            Assert.Equal("机床2", result[1].MachineName);
            Assert.Equal(50, result[1].TotalQuantity);
        }

        // ======== GetWorkerSummary ========

        [Fact]
        public void GetWorkerSummary_无数据_返回0值()
        {
            var result = _service.GetWorkerSummary(null, null);
            Assert.NotNull(result);
            Assert.Equal(0, result.TotalQuantity);
            Assert.Equal(0, result.ActiveWorkerCount);
        }

        // ======== GetWorkerList ========

        [Fact]
        public void GetWorkerList_无数据_返回空列表()
        {
            var result = _service.GetWorkerList(null, null, null);
            Assert.NotNull(result);
            Assert.Empty(result);
        }

        // ======== GetProgramSummary ========

        [Fact]
        public void GetProgramSummary_无数据_返回0值()
        {
            var result = _service.GetProgramSummary(null, null, null);
            Assert.NotNull(result);
            Assert.Equal(0, result.TotalQuantity);
            Assert.Equal(0, result.RunningProgramCount);
        }

        // ======== GetProgramList ========

        [Fact]
        public void GetProgramList_无数据_返回空列表()
        {
            var result = _service.GetProgramList(null, null, null);
            Assert.NotNull(result);
            Assert.Empty(result);
        }
    }
}
