using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Text;
using Haoliang.Models.User;
using Haoliang.Data.Repositories;
using Haoliang.Data.Entities;

namespace Haoliang.Data.Repositories
{
    public interface IUserRepository : IRepository<User>
    {
        Task<User> GetByUsernameAsync(string username);
        Task<User> GetByEmailAsync(string email);
        Task<bool> UsernameExistsAsync(string username);
        Task<bool> EmailExistsAsync(string email);
        Task<bool> ValidatePasswordAsync(string username, string password);
        Task<User> AuthenticateAsync(string username, string password);
        Task<IEnumerable<User>> GetByRoleIdAsync(int roleId);
        Task UpdateLastLoginAsync(int userId);
        Task<bool> ChangePasswordAsync(int userId, string oldPassword, string newPassword);
        Task<bool> ResetPasswordAsync(int userId, string newPassword);
        Task<IEnumerable<User>> GetActiveUsersAsync();
        Task<bool> IsUserActiveAsync(string username);
    }

    public class UserRepository : Repository<User>, IUserRepository
    {
        private readonly CNCDbContext _context;

        public UserRepository(CNCDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<User> GetByUsernameAsync(string username)
        {
            return await _context.Users
                .Include(u => u.Role)
                .FirstOrDefaultAsync(u => u.Username == username);
        }

        public async Task<User> GetByEmailAsync(string email)
        {
            return await _context.Users
                .Include(u => u.Role)
                .FirstOrDefaultAsync(u => u.Email == email);
        }

        public async Task<bool> UsernameExistsAsync(string username)
        {
            return await _context.Users
                .AnyAsync(u => u.Username == username);
        }

        public async Task<bool> EmailExistsAsync(string email)
        {
            return await _context.Users
                .AnyAsync(u => u.Email == email);
        }

        public async Task<bool> ValidatePasswordAsync(string username, string password)
        {
            var user = await GetByUsernameAsync(username);
            if (user == null || !user.IsActive)
                return false;

            return VerifyPassword(password, user.PasswordHash);
        }

        public async Task<User> AuthenticateAsync(string username, string password)
        {
            var user = await GetByUsernameAsync(username);
            if (user == null || !user.IsActive)
                return null;

            if (VerifyPassword(password, user.PasswordHash))
            {
                await UpdateLastLoginAsync(user.Id);
                return user;
            }

            return null;
        }

        public async Task<IEnumerable<User>> GetByRoleIdAsync(int roleId)
        {
            return await _context.Users
                .Include(u => u.Role)
                .Where(u => u.RoleId == roleId)
                .OrderBy(u => u.Username)
                .ToListAsync();
        }

        public async Task UpdateLastLoginAsync(int userId)
        {
            var user = await GetByIdAsync(userId);
            if (user != null)
            {
                user.LastLoginTime = DateTime.Now;
                user.UpdatedAt = DateTime.Now;
                
                Update(user);
                await SaveAsync();
            }
        }

        public async Task<bool> ChangePasswordAsync(int userId, string oldPassword, string newPassword)
        {
            var user = await GetByIdAsync(userId);
            if (user == null)
                return false;

            if (!VerifyPassword(oldPassword, user.PasswordHash))
                return false;

            user.PasswordHash = HashPassword(newPassword);
            user.UpdatedAt = DateTime.Now;
            
            Update(user);
            await SaveAsync();
            return true;
        }

        public async Task<bool> ResetPasswordAsync(int userId, string newPassword)
        {
            var user = await GetByIdAsync(userId);
            if (user == null)
                return false;

            user.PasswordHash = HashPassword(newPassword);
            user.UpdatedAt = DateTime.Now;
            
            Update(user);
            await SaveAsync();
            return true;
        }

        public async Task<IEnumerable<User>> GetActiveUsersAsync()
        {
            return await _context.Users
                .Include(u => u.Role)
                .Where(u => u.IsActive)
                .OrderBy(u => u.Username)
                .ToListAsync();
        }

        public async Task<bool> IsUserActiveAsync(string username)
        {
            var user = await GetByUsernameAsync(username);
            return user != null && user.IsActive;
        }

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

        private bool VerifyPassword(string password, string hash)
        {
            return HashPassword(password) == hash;
        }
    }

    public interface IRoleRepository : IRepository<Role>
    {
        Task<Role> GetByNameAsync(string roleName);
        Task<bool> RoleNameExistsAsync(string roleName);
        Task<IEnumerable<Permission>> GetRolePermissionsAsync(int roleId);
        Task<bool> AddPermissionToRoleAsync(int roleId, int permissionId);
        Task<bool> RemovePermissionFromRoleAsync(int roleId, int permissionId);
        Task<bool> UserHasPermissionAsync(int userId, string permissionName);
    }

    public class RoleRepository : Repository<Role>, IRoleRepository
    {
        private readonly CNCDbContext _context;

        public RoleRepository(CNCDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<Role> GetByNameAsync(string roleName)
        {
            return await _context.Roles
                .FirstOrDefaultAsync(r => r.RoleName == roleName);
        }

        public async Task<bool> RoleNameExistsAsync(string roleName)
        {
            return await _context.Roles
                .AnyAsync(r => r.RoleName == roleName);
        }

        public async Task<IEnumerable<Permission>> GetRolePermissionsAsync(int roleId)
        {
            var role = await GetByIdAsync(roleId);
            if (role == null)
                return new List<Permission>();

            var permissionIds = role.Permissions.Select(p => int.Parse(p.Split('_')[0])).ToList();
            return await _context.Permissions
                .Where(p => permissionIds.Contains(p.Id))
                .ToListAsync();
        }

        public async Task<bool> AddPermissionToRoleAsync(int roleId, int permissionId)
        {
            var role = await GetByIdAsync(roleId);
            if (role == null)
                return false;

            if (!role.Permissions.Contains(permissionId.ToString()))
            {
                role.Permissions.Add(permissionId.ToString());
                role.UpdatedAt = DateTime.Now;
                
                Update(role);
                await SaveAsync();
                return true;
            }

            return false;
        }

        public async Task<bool> RemovePermissionFromRoleAsync(int roleId, int permissionId)
        {
            var role = await GetByIdAsync(roleId);
            if (role == null)
                return false;

            if (role.Permissions.Contains(permissionId.ToString()))
            {
                role.Permissions.Remove(permissionId.ToString());
                role.UpdatedAt = DateTime.Now;
                
                Update(role);
                await SaveAsync();
                return true;
            }

            return false;
        }

        public async Task<bool> UserHasPermissionAsync(int userId, string permissionName)
        {
            var user = await _context.Users
                .Include(u => u.Role)
                .FirstOrDefaultAsync(u => u.Id == userId);
            
            if (user == null || user.Role == null)
                return false;

            return user.Role.Permissions.Contains(permissionName);
        }
    }

    public interface IEmployeeRepository : IRepository<Employee>
    {
        Task<Employee> GetByEmployeeCodeAsync(string employeeCode);
        Task<bool> EmployeeCodeExistsAsync(string employeeCode);
        Task<IEnumerable<Employee>> GetByDepartmentAsync(string department);
        Task<IEnumerable<Employee>> GetByPositionAsync(string position);
        Task<Employee> GetAssignedEmployeeAsync(int deviceId);
        Task<bool> AssignDeviceToEmployeeAsync(int employeeId, int deviceId);
        Task<bool> UnassignDeviceFromEmployeeAsync(int employeeId, int deviceId);
    }

    public class EmployeeRepository : Repository<Employee>, IEmployeeRepository
    {
        private readonly CNCDbContext _context;

        public EmployeeRepository(CNCDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<Employee> GetByEmployeeCodeAsync(string employeeCode)
        {
            return await _context.Employees
                .FirstOrDefaultAsync(e => e.EmployeeCode == employeeCode);
        }

        public async Task<bool> EmployeeCodeExistsAsync(string employeeCode)
        {
            return await _context.Employees
                .AnyAsync(e => e.EmployeeCode == employeeCode);
        }

        public async Task<IEnumerable<Employee>> GetByDepartmentAsync(string department)
        {
            return await _context.Employees
                .Where(e => e.Department == department)
                .OrderBy(e => e.Name)
                .ToListAsync();
        }

        public async Task<IEnumerable<Employee>> GetByPositionAsync(string position)
        {
            return await _context.Employees
                .Where(e => e.Position == position)
                .OrderBy(e => e.Name)
                .ToListAsync();
        }

        public async Task<Employee> GetAssignedEmployeeAsync(int deviceId)
        {
            var assignment = await _context.DeviceAssignments
                .FirstOrDefaultAsync(da => da.DeviceId == deviceId && da.EndDate == null);
            
            if (assignment != null)
            {
                return await GetByIdAsync(assignment.EmployeeId);
            }

            return null;
        }

        public async Task<bool> AssignDeviceToEmployeeAsync(int employeeId, int deviceId)
        {
            var existingAssignment = await _context.DeviceAssignments
                .FirstOrDefaultAsync(da => da.DeviceId == deviceId && da.EndDate == null);

            if (existingAssignment != null)
                return false;

            var assignment = new DeviceAssignment
            {
                EmployeeId = employeeId,
                DeviceId = deviceId,
                AssignmentDate = DateTime.Now,
                CreatedAt = DateTime.Now
            };

            await _context.DeviceAssignments.AddAsync(assignment);
            await SaveAsync();
            return true;
        }

        public async Task<bool> UnassignDeviceFromEmployeeAsync(int employeeId, int deviceId)
        {
            var assignment = await _context.DeviceAssignments
                .FirstOrDefaultAsync(da => 
                    da.EmployeeId == employeeId && 
                    da.DeviceId == deviceId && 
                    da.EndDate == null);

            if (assignment != null)
            {
                assignment.EndDate = DateTime.Now;
                _context.DeviceAssignments.Update(assignment);
                await SaveAsync();
                return true;
            }

            return false;
        }
    }
}