using System;
using System.Collections.Generic;
using Xunit;
using CncModels.Dto.Dashboard;
using CncModels.Entity;
using CncRepository.Interface;
using CncService.Interface;
using CncService.Impl;

namespace CncService.Tests
{
    // Fake repositories to isolate DashboardService.GetCollectorStatus tests
    public class FakeDashboardRepository : IDashboardRepository
    {
        public DashboardSummaryResponse GetSummary() => new DashboardSummaryResponse();
        public List<WorkshopProductionResponse> GetWorkshopProduction(DateTime startDate, DateTime endDate) => new List<WorkshopProductionResponse>();
        public List<MachineRankResponse> GetMachineRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc") => new List<MachineRankResponse>();
        public List<WorkerRankResponse> GetWorkerRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc") => new List<WorkerRankResponse>();
        public List<dynamic> GetProductionTrend(int days) => new List<dynamic>();
        public object GetMachineStatusDistribution() => new object();
        public List<AlertListItem> GetRecentAlerts(int count) => new List<AlertListItem>();
        public List<ProgramRankItem> GetProgramRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc") => new List<ProgramRankItem>();
        public List<ProgramDistributionItem> GetProgramDistribution(DateTime startDate, DateTime endDate, int top = 10) => new List<ProgramDistributionItem>();
    }

    public class FakeCollectorHeartbeatRepository : ICollectorHeartbeatRepository
    {
        private readonly CollectorHeartbeat _latest;
        public FakeCollectorHeartbeatRepository(CollectorHeartbeat latest) { _latest = latest; }
        public long Create(CollectorHeartbeat entity) => 1;
        public CollectorHeartbeat GetLatest(string serviceId) => _latest;
        public int DeleteBeforeDate(DateTime date) => 0;
    }

    public class FakeWindowsServiceChecker : IWindowsServiceChecker
    {
        private readonly ServiceStatusEnum _status;
        public FakeWindowsServiceChecker(ServiceStatusEnum status) { _status = status; }
        public ServiceStatusEnum GetServiceStatus(string serviceName) => _status;
        public (bool, string) TryStartService(string serviceName, int timeoutSeconds) => (
            _status == ServiceStatusEnum.NotInstalled ? false : true,
            _status == ServiceStatusEnum.NotInstalled ? "NotInstalled" : "Started");
        public (bool, string) TryStopService(string serviceName, int timeoutSeconds) => (
            true, "Stopped");
    }

    public class FakeSysConfigRepository : ISysConfigRepository
    {
        public SysConfig GetByKey(string configKey) => new SysConfig { ConfigKey = configKey, ConfigValue = "300" };
        public List<SysConfig> GetAll() => new List<SysConfig>();
        public bool UpdateValue(int id, string value) => true;
    }

    [Collection("Database")]
    public class DashboardServiceTests : IDisposable
    {
        private readonly DashboardService _service;

        public DashboardServiceTests()
        {
            TestDb.TruncateAll();
            _service = ServiceFactory.CreateDashboardService();
        }

        public void Dispose()
        {
            TestDb.TruncateAll();
        }
        [Fact]
        public void GetCollectorStatus_With_NotInstalled_Service_Returns_NotInstalled_State()
        {
            // Arrange
            var latest = new CollectorHeartbeat { Id = 1, ServiceId = "collector-service", Status = "running", UptimeSeconds = 120, LastCollectTime = DateTime.Now, CreatedAt = DateTime.Now };
            var dashboardRepo = new FakeDashboardRepository();
            var heartbeatRepo = new FakeCollectorHeartbeatRepository(latest);
            var checker = new FakeWindowsServiceChecker(CncService.Interface.ServiceStatusEnum.NotInstalled);

            var svc = new DashboardService(dashboardRepo, heartbeatRepo, new FakeSysConfigRepository(), checker);

            // Act
            var resultObj = svc.GetCollectorStatus();
            var t = resultObj.GetType();
            var statusProp = t.GetProperty("status");
            var serviceStatusProp = t.GetProperty("serviceStatus");
            var uptimeProp = t.GetProperty("uptimeSeconds");
            var lastCollectTimeProp = t.GetProperty("lastCollectTime");
            Assert.NotNull(statusProp);
            Assert.NotNull(serviceStatusProp);
            var statusVal = statusProp.GetValue(resultObj) as string;
            var serviceStatusVal = serviceStatusProp.GetValue(resultObj) as string;
            Assert.Equal("not_installed", statusVal);
            Assert.Equal("NotInstalled", serviceStatusVal);
        }

        [Fact]
        public void GetCollectorStatus_With_Running_Heartbeats_Returns_Running_State()
        {
            var latest = new CollectorHeartbeat { Id = 1, ServiceId = "collector-service", Status = "running", UptimeSeconds = 60, LastCollectTime = DateTime.Now, CreatedAt = DateTime.Now };
            var dashboardRepo = new FakeDashboardRepository();
            var heartbeatRepo = new FakeCollectorHeartbeatRepository(latest);
            var checker = new FakeWindowsServiceChecker(CncService.Interface.ServiceStatusEnum.Running);
            var svc = new DashboardService(dashboardRepo, heartbeatRepo, new FakeSysConfigRepository(), checker);

            var resultObj = svc.GetCollectorStatus();
            var t = resultObj.GetType();
            var serviceStatusProp = t.GetProperty("serviceStatus");
            var statusProp = t.GetProperty("status");
            var serviceStatusVal = serviceStatusProp.GetValue(resultObj) as string;
            var statusVal = statusProp.GetValue(resultObj) as string;
            Assert.Equal("Running", serviceStatusVal);
            Assert.Equal("running", statusVal);
        }

        [Fact]
        public void GetCollectorStatus_With_Starting_ServiceStatus_Returns_Starting_State()
        {
            var latest = new CollectorHeartbeat { Id = 2, ServiceId = "collector-service", Status = "running", UptimeSeconds = 120, LastCollectTime = DateTime.Now, CreatedAt = DateTime.Now };
            var dashboardRepo = new FakeDashboardRepository();
            var heartbeatRepo = new FakeCollectorHeartbeatRepository(latest);
            var checker = new FakeWindowsServiceChecker(CncService.Interface.ServiceStatusEnum.Starting);
            var svc = new DashboardService(dashboardRepo, heartbeatRepo, new FakeSysConfigRepository(), checker);

            var resultObj = svc.GetCollectorStatus();
            var t = resultObj.GetType();
            var serviceStatusProp = t.GetProperty("serviceStatus");
            var statusProp = t.GetProperty("status");
            var serviceStatusVal = serviceStatusProp.GetValue(resultObj) as string;
            var statusVal = statusProp.GetValue(resultObj) as string;
            Assert.Equal("Starting", serviceStatusVal);
            Assert.Equal("running", statusVal);
        }

        // ======== GetProgramRank ========

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

        [Fact]
        public void GetProgramRank_有数据_返回排行列表()
        {
            // 插入测试数据
            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(), 'O0001', NOW(), 0, 50, 50, 1, NOW(), NOW())");

            var result = _service.GetProgramRank(DateTime.Today, DateTime.Today);
            Assert.NotEmpty(result);
            Assert.Equal("O0001", result[0].ProgramName);
            Assert.Equal(150, result[0].TotalQuantity);
            Assert.Equal(2, result[0].MachineCount);
            Assert.Equal(1, result[0].Rank);
        }

        // ======== GetProgramDistribution ========

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

        [Fact]
        public void GetProgramDistribution_有数据_返回分布列表()
        {
            // 插入测试数据
            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_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, 80, 80, 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(), 'O0002', NOW(), 0, 20, 20, 1, NOW(), NOW())");

            var result = _service.GetProgramDistribution(DateTime.Today, DateTime.Today);
            Assert.NotEmpty(result);
            Assert.Equal(2, result.Count);
            Assert.Equal("O0001", result[0].ProgramName);
            Assert.Equal(80, result[0].TotalQuantity);
            Assert.Equal(80m, result[0].Percentage);
            Assert.Equal("O0002", result[1].ProgramName);
            Assert.Equal(20, result[1].TotalQuantity);
            Assert.Equal(20m, result[1].Percentage);
        }
    }
}
