using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Services.Interfaces;

namespace MilitaryTrainingManagement.Services.Implementations;

/// <summary>
/// 认证服务实现
/// </summary>
public class AuthenticationService : IAuthenticationService
{
    private readonly ApplicationDbContext _context;
    private readonly IConfiguration _configuration;

    public AuthenticationService(ApplicationDbContext context, IConfiguration configuration)
    {
        _context = context;
        _configuration = configuration;
    }

    public async Task<(UserAccount? User, string? Token)> LoginAsync(string username, string password)
    {
        var user = await _context.UserAccounts
            .Include(u => u.OrganizationalUnit)
            .FirstOrDefaultAsync(u => u.Username == username && u.IsActive);

        if (user == null || !ValidatePassword(password, user.PasswordHash))
        {
            return (null, null);
        }

        await UpdateLastLoginAsync(user.Id);
        var token = GenerateJwtToken(user);
        return (user, token);
    }

    public async Task<UserAccount?> GetUserByIdAsync(int userId)
    {
        return await _context.UserAccounts
            .Include(u => u.OrganizationalUnit)
            .FirstOrDefaultAsync(u => u.Id == userId);
    }

    public async Task<UserAccount?> GetUserByUsernameAsync(string username)
    {
        return await _context.UserAccounts
            .Include(u => u.OrganizationalUnit)
            .FirstOrDefaultAsync(u => u.Username == username);
    }

    public async Task<UserAccount> CreateUserAsync(string username, string password, string displayName, int organizationalUnitId)
    {
        var user = new UserAccount
        {
            Username = username,
            PasswordHash = HashPassword(password),
            DisplayName = displayName,
            OrganizationalUnitId = organizationalUnitId,
            IsActive = true,
            CreatedAt = DateTime.UtcNow
        };

        _context.UserAccounts.Add(user);
        await _context.SaveChangesAsync();
        return user;
    }

    public async Task UpdateLastLoginAsync(int userId)
    {
        var user = await _context.UserAccounts.FindAsync(userId);
        if (user != null)
        {
            user.LastLoginAt = DateTime.UtcNow;
            await _context.SaveChangesAsync();
        }
    }

    public string GenerateJwtToken(UserAccount user)
    {
        var key = _configuration["Jwt:Key"] ?? throw new InvalidOperationException("JWT Key not configured");
        var issuer = _configuration["Jwt:Issuer"] ?? "MilitaryTrainingManagement";
        var audience = _configuration["Jwt:Audience"] ?? "MilitaryTrainingManagementUsers";
        var expirationMinutes = int.Parse(_configuration["Jwt:ExpirationInMinutes"] ?? "480");

        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim("userId", user.Id.ToString()),
            new Claim("username", user.Username),
            new Claim("unitId", user.OrganizationalUnitId.ToString()),
            new Claim("unitLevel", ((int)user.OrganizationalUnit.Level).ToString()),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(expirationMinutes),
            signingCredentials: credentials);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    public bool ValidatePassword(string password, string passwordHash)
    {
        return HashPassword(password) == passwordHash;
    }

    public string HashPassword(string password)
    {
        using var sha256 = SHA256.Create();
        var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
        return Convert.ToBase64String(hashedBytes);
    }
}
