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

namespace CncService.Tests
{
    /// <summary>
    /// AuthService 登录认证测试
    /// 测试场景：登录成功、密码错误、用户名错误、参数为空、记住密码、构造函数参数校验
    /// </summary>
    [Collection("Database")]
    public class AuthServiceTests : IDisposable
    {
        private readonly AuthService _service;

        public AuthServiceTests()
        {
            TestDb.TruncateAll();
            _service = ServiceFactory.CreateAuthService();
        }

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

        // ======== 构造函数校验 ========

        [Fact]
        public void 构造函数_SysConfigRepository为null_抛出ArgumentNullException()
        {
            Assert.Throws<ArgumentNullException>(() => new AuthService(null, "secret"));
        }

        [Fact]
        public void 构造函数_JwtSecret为null_抛出ArgumentNullException()
        {
            var repo = new CncRepository.Impl.SysConfigRepository(TestDb.ConnectionString);
            Assert.Throws<ArgumentNullException>(() => new AuthService(repo, null));
        }

        // ======== 登录成功 ========

        [Fact]
        public void Login_正确的用户名密码_返回Token和有效期()
        {
            // 设置真实BCrypt密码
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            var response = svc.Login(new LoginRequest
            {
                Username = "admin",
                Password = plainPwd
            });

            Assert.NotNull(response);
            Assert.False(string.IsNullOrWhiteSpace(response.Token), "Token不应为空");
            Assert.Equal(8 * 3600, response.ExpiresIn); // 默认8小时
        }

        [Fact]
        public void Login_记住密码_有效期24小时()
        {
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            var response = svc.Login(new LoginRequest
            {
                Username = "admin",
                Password = plainPwd,
                RememberMe = true
            });

            Assert.Equal(24 * 3600, response.ExpiresIn);
        }

        // ======== 登录失败 ========

        [Fact]
        public void Login_密码错误_抛出BusinessException()
        {
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            var ex = Assert.Throws<BusinessException>(() => svc.Login(new LoginRequest
            {
                Username = "admin",
                Password = "wrongpassword"
            }));

            Assert.Equal(ErrorCode.BadRequest, ex.Code);
            Assert.Contains("用户名或密码错误", ex.Message);
        }

        [Fact]
        public void Login_用户名错误_抛出BusinessException()
        {
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            var ex = Assert.Throws<BusinessException>(() => svc.Login(new LoginRequest
            {
                Username = "wronguser",
                Password = plainPwd
            }));

            Assert.Equal(ErrorCode.BadRequest, ex.Code);
        }

        [Fact]
        public void Login_用户名大小写不敏感_登录成功()
        {
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            var response = svc.Login(new LoginRequest
            {
                Username = "ADMIN",
                Password = plainPwd
            });

            Assert.NotNull(response.Token);
        }

        // ======== 参数校验 ========

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

        [Fact]
        public void Login_密码为null_BCrypt验证失败()
        {
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            // Password为null，BCrypt.Verify("", hash) 应返回false
            var ex = Assert.Throws<BusinessException>(() => svc.Login(new LoginRequest
            {
                Username = "admin",
                Password = null
            }));
            Assert.Equal(ErrorCode.BadRequest, ex.Code);
        }

        // ======== 边界情况 ========

        [Fact]
        public void Login_数据库无配置_抛出BusinessException()
        {
            // 清空sys_config表
            TestDb.Execute("DELETE FROM cnc_sys_config");

            var svc = ServiceFactory.CreateAuthService();
            var ex = Assert.Throws<BusinessException>(() => svc.Login(new LoginRequest
            {
                Username = "admin",
                Password = "admin123"
            }));

            Assert.Equal(ErrorCode.BadRequest, ex.Code);
        }

        [Fact]
        public void Login_Token格式正确_三段式Base64Url()
        {
            const string plainPwd = "admin123";
            TestDb.SetRealPasswordHash(plainPwd);

            var svc = ServiceFactory.CreateAuthService();
            var response = svc.Login(new LoginRequest
            {
                Username = "admin",
                Password = plainPwd
            });

            // JWT格式：header.payload.signature，用点分隔为3段
            var parts = response.Token.Split('.');
            Assert.Equal(3, parts.Length);
            Assert.False(string.IsNullOrWhiteSpace(parts[0]), "Header不应为空");
            Assert.False(string.IsNullOrWhiteSpace(parts[1]), "Payload不应为空");
            Assert.False(string.IsNullOrWhiteSpace(parts[2]), "Signature不应为空");
        }
    }
}
