using Health.Application.Auth;
using Health.Infrastructure.Services;

namespace Health.Infrastructure.Auth;

public sealed class AuthService(AppDbContext db, JwtProvider jwt, SmsService sms) : IAuthService
{
    private const string AdminPhone = "12345678910";
    private const string AdminSmsCode = "000000";
    private static readonly Guid AdminId = Guid.Parse("00000000-0000-0000-0000-000000000001");
    private readonly AppDbContext _db = db;
    private readonly JwtProvider _jwt = jwt;
    private readonly SmsService _sms = sms;

    public async Task<AuthResult> SendCodeAsync(string phone, bool revealDevelopmentCode, CancellationToken ct)
    {
        var code = phone == AdminPhone ? AdminSmsCode : _sms.GenerateCode();
        await _db.VerificationCodes.AddAsync(new VerificationCode
        {
            Id = Guid.NewGuid(), Phone = phone, Code = code,
            ExpiresAt = DateTime.UtcNow.AddMinutes(5),
        }, ct);
        await _db.SaveChangesAsync(ct);
        if (phone != AdminPhone) await _sms.SendCodeAsync(phone, code);
        return new AuthResult(0, new { success = true, devCode = revealDevelopmentCode ? code : null });
    }

    public async Task<AuthResult> RegisterAsync(RegisterCommand command, CancellationToken ct)
    {
        var code = await TakeCodeAsync(command.Phone, command.SmsCode, ct);
        if (code == null) return Error(40001, "验证码错误或已过期");
        if (await _db.Users.AnyAsync(x => x.Phone == command.Phone, ct)) return Error(40002, "该手机号已注册，请直接登录");
        if (string.IsNullOrWhiteSpace(command.Name)) return Error(40003, "请输入姓名");
        if (!await _db.Doctors.AnyAsync(x => x.Id == command.DoctorId && x.IsActive, ct)) return Error(40004, "所选医生不存在或已停诊");

        code.IsUsed = true;
        var user = new User
        {
            Id = Guid.NewGuid(), Phone = command.Phone, Role = "User", Name = command.Name,
            DoctorId = command.DoctorId, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow,
        };
        _db.Users.Add(user);
        _db.NotificationPreferences.Add(new NotificationPreference { Id = Guid.NewGuid(), UserId = user.Id });
        _db.HealthArchives.Add(new HealthArchive { Id = Guid.NewGuid(), UserId = user.Id });
        var tokens = AddTokens(user.Id, user.Phone, user.Role);
        await _db.SaveChangesAsync(ct);
        return new AuthResult(0, new { tokens.accessToken, tokens.refreshToken, user = new { user.Id, user.Phone, user.Role, isNew = true } });
    }

    public async Task<AuthResult> LoginAsync(string phone, string smsCode, CancellationToken ct)
    {
        var code = await TakeCodeAsync(phone, smsCode, ct);
        if (code == null) return Error(40001, "验证码错误或已过期");
        code.IsUsed = true;

        if (phone == AdminPhone)
        {
            var tokens = AddTokens(AdminId, AdminPhone, "Admin");
            await _db.SaveChangesAsync(ct);
            return new AuthResult(0, new { tokens.accessToken, tokens.refreshToken, user = new { id = AdminId, phone = AdminPhone, role = "Admin", name = "管理员", isNew = false } });
        }

        var user = await _db.Users.FirstOrDefaultAsync(x => x.Phone == phone, ct);
        if (user == null) return Error(40004, "该手机号未注册，请先注册");
        user.UpdatedAt = DateTime.UtcNow;
        var userTokens = AddTokens(user.Id, user.Phone, user.Role);
        await _db.SaveChangesAsync(ct);
        return new AuthResult(0, new
        {
            userTokens.accessToken, userTokens.refreshToken,
            user = new { user.Id, user.Phone, user.Role, user.Name, user.Gender, user.AvatarUrl, BirthDate = user.BirthDate?.ToString("yyyy-MM-dd"), isNew = false }
        });
    }

    public async Task<AuthResult> RefreshAsync(string refreshToken, CancellationToken ct)
    {
        var oldToken = await _db.RefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshToken && !x.IsRevoked, ct);
        if (oldToken == null || oldToken.ExpiresAt < DateTime.UtcNow) return Error(40002, "登录已过期，请重新登录");
        oldToken.IsRevoked = true;

        if (oldToken.UserId == AdminId)
        {
            var tokens = AddTokens(AdminId, AdminPhone, "Admin");
            await _db.SaveChangesAsync(ct);
            return new AuthResult(0, new { tokens.accessToken, tokens.refreshToken, user = new { role = "Admin" } });
        }

        var user = await _db.Users.FindAsync([oldToken.UserId], ct);
        if (user == null) return Error(40002, "用户不存在");
        var userTokens = AddTokens(user.Id, user.Phone, user.Role);
        await _db.SaveChangesAsync(ct);
        return new AuthResult(0, new { userTokens.accessToken, userTokens.refreshToken, user = new { user.Role } });
    }

    public async Task LogoutAsync(string refreshToken, CancellationToken ct)
    {
        var token = await _db.RefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshToken, ct);
        if (token != null) token.IsRevoked = true;
        await _db.SaveChangesAsync(ct);
    }

    private Task<VerificationCode?> TakeCodeAsync(string phone, string code, CancellationToken ct) =>
        _db.VerificationCodes.Where(x => x.Phone == phone && x.Code == code && x.ExpiresAt > DateTime.UtcNow && !x.IsUsed)
            .OrderByDescending(x => x.CreatedAt).FirstOrDefaultAsync(ct);

    private (string accessToken, string refreshToken) AddTokens(Guid userId, string phone, string role)
    {
        var access = _jwt.GenerateAccessToken(userId, phone, role);
        var refresh = _jwt.GenerateRefreshToken();
        _db.RefreshTokens.Add(new RefreshToken { Id = Guid.NewGuid(), UserId = userId, Token = refresh, ExpiresAt = DateTime.UtcNow.AddDays(30) });
        return (access, refresh);
    }

    private static AuthResult Error(int code, string message) => new(code, null, message);
}
