using LSFE.Domain.Contracts;
using LSFE.Infrastructure.Database;
using LSFE.Infrastructure.Dto;
using LSFE.Infrastructure.Entities;
using LSFE.Infrastructure.Entities.Auth;
using LSFE.Infrastructure.Model;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace LSFE.Domain.Services
{
    public class AccountService : IAccountService
    {
        private readonly UserManager<AppsUsers> _userManager;
        private readonly IConfiguration _configuration;
        private readonly RoleManager<Role> _roleManager;
        private readonly LSFEDbContext _context;
        public AccountService(UserManager<AppsUsers> userManager, IConfiguration configuration,
            RoleManager<Role> roleManager , LSFEDbContext context )
        {
            _userManager = userManager;
            _configuration = configuration;
            _roleManager = roleManager;
            _context = context;
        }
        public async Task<Response> AddUserToRoleAsync(string userId, string roleName)
        {
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
                return new Response
                {
                    Success = false,
                    Message = "User not found",
                    MessCode = 0
                }; 

            // Ensure role exists
            if (!await _roleManager.RoleExistsAsync(roleName))
            {
                var roleResult = await _roleManager.CreateAsync(new Role());
                if (!roleResult.Succeeded)
                    return new Response
                    {
                        Success = false,
                        Message = roleResult.ToString(),
                        MessCode = 0
                    }; 
            }

             await _userManager.AddToRoleAsync(user, roleName);
            return new Response
            {
                Success = true,
                Message = "Add user role successfully!",
                MessCode = 1
            };
        }

        public async Task<Response> AddClaimToUserAsync(string userId, Claim claim)
        {
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
                return new Response
                {
                    Success = false,
                    Message = "User not found",
                    MessCode = 0
                };

            await _userManager.AddClaimAsync(user, claim);
            return new Response
            {
                Success = true,
                Message = "Success",
                MessCode = 1
            };
        }
        public async Task<Response> RegisterUserAsync(RegisterUserDto dto)
        {
            try
            {
                // 🔍 1. Validate required fields
                if (string.IsNullOrWhiteSpace(dto.UserName) ||
                    string.IsNullOrWhiteSpace(dto.Email) ||
                    string.IsNullOrWhiteSpace(dto.Password))
                {
                    return new Response
                    {
                        Success = false,
                        Message = "Username, Email, and Password are required.",
                        MessCode = 0
                    };
                }

                // 🧩 2. Check if username or email already exists
                var existingUserByName = await _userManager.FindByNameAsync(dto.UserName);
                if (existingUserByName != null)
                {
                    return new Response
                    {
                        Success = false,
                        Message = $"Username '{dto.UserName}' is already taken.",
                        MessCode = 0
                    };
                }

                var existingUserByEmail = await _userManager.FindByEmailAsync(dto.Email);
                if (existingUserByEmail != null)
                {
                    return new Response
                    {
                        Success = false,
                        Message = $"Email '{dto.Email}' is already registered.",
                        MessCode = 0
                    };
                }

                // 🏢 3. Optional: Validate Department
                if (dto.DepartmentId.HasValue)
                {
                    var departmentExists = await _context.Departments
                        .AnyAsync(d => d.DepartmentId == dto.DepartmentId && d.IsActive);

                    if (!departmentExists)
                    {
                        return new Response
                        {
                            Success = false,
                            Message = "Invalid Department selected.",
                            MessCode = 0
                        };
                    }
                }

                // 🧍‍♂️ 4. Create new user
                var user = new AppsUsers
                {
                    UserName = dto.UserName,
                    Email = dto.Email,
                    FullName = dto.FullName,
                    EmployeeId = dto.EmployeeId,
                    Company = dto.Company,
                    DepartmentId = dto.DepartmentId,
                    Address = dto.Address,
                    PhoneNumber = dto.PhoneNumber,
                    CreatedDate = DateTime.UtcNow,
                    UpdatedDate = DateTime.UtcNow,
                    CreatedBy = dto.UpdatedBy,
                    UpdatedBy = dto.UpdatedBy,
                    EmailConfirmed = true // optional: mark as verified by admin
                };

                var createResult = await _userManager.CreateAsync(user, dto.Password);
                if (!createResult.Succeeded)
                {
                    var errors = string.Join("; ", createResult.Errors.Select(e => e.Description));
                    return new Response
                    {
                        Success = false,
                        Message = $"Failed to create user: {errors}",
                        MessCode = 0
                    };
                }

                // 🛡️ 5. Assign Role if provided
                if (!string.IsNullOrWhiteSpace(dto.RoleId))
                {
                    var role = await _roleManager.FindByIdAsync(dto.RoleId);
                    if (role == null)
                    {
                        return new Response
                        {
                            Success = false,
                            Message = $"Role with ID '{dto.RoleId}' does not exist.",
                            MessCode = 0
                        };
                    }

                    await _userManager.AddToRoleAsync(user, role.Name);

                    // optional: add user claim for role
                    await _userManager.AddClaimAsync(user, new Claim("RoleName", role.Name));
                }

                return new Response
                {
                    Success = true,
                    Message = "User registered successfully!",
                    MessCode = 1
                };
            }
            catch (Exception ex)
            {
                return new Response
                {
                    Success = false,
                    Message = $"Unexpected error: {ex.Message}",
                    MessCode = 0
                };
            }
        }
        public async Task<string> CreateToken(AppsUsers user)
        {
            var authClaims = await BuildClaims(user);

            var authSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));

            var token = new JwtSecurityToken(
                issuer: _configuration["JWT:ValidIssuer"],
                audience: _configuration["JWT:ValidAudience"],
                expires: DateTime.UtcNow.AddMinutes(30),
                claims: authClaims,
                signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
            );

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

        private async Task<List<Claim>> BuildClaims(AppsUsers user)
        {
            var roles = await _userManager.GetRolesAsync(user);

            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(ClaimTypes.NameIdentifier, user.Id), 
                new Claim("FullName", user.FullName ?? ""), 
                 new Claim("Company", user.Company ?? ""),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            };

            claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
            return claims;
        }

        public async Task<AppsUsers?> GetUserById(string userId)
        {
            return await _userManager.Users.FirstOrDefaultAsync(u => u.Id == userId);
        }

        public async Task<Response> UpdateUserAsync(UpdateUserDto dto)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                var user = await _userManager.FindByIdAsync(dto.Id.ToString());
                if (user == null)
                    return new Response { Success = false, Message = "User not found.", MessCode = 0 };

                // --- Update basic info ---
                user.Email = dto.Email;
                user.FullName = dto.FullName;
                user.EmployeeId = dto.EmployeeId;
                user.Company = dto.Company;
                user.DepartmentId = dto.DepartmentId;
                user.Address = dto.Address;
                user.PhoneNumber = dto.PhoneNumber;
                user.UpdatedBy = dto.UpdatedBy;
                user.UpdatedDate = DateTime.UtcNow;

                // --- Update password (if provided) ---
                if (!string.IsNullOrWhiteSpace(dto.Password))
                {
                    var passwordHasher = new PasswordHasher<AppsUsers>();
                    user.PasswordHash = passwordHasher.HashPassword(user, dto.Password);
                }

                var updateResult = await _userManager.UpdateAsync(user);
                if (!updateResult.Succeeded)
                    return new Response
                    {
                        Success = false,
                        Message = string.Join(", ", updateResult.Errors.Select(e => e.Description)),
                        MessCode = 0
                    };

                // --- Update roles ---
                if (!string.IsNullOrEmpty(dto.RoleName))
                {
                    var currentRoles = await _userManager.GetRolesAsync(user);
                    if (currentRoles.Any())
                        await _userManager.RemoveFromRolesAsync(user, currentRoles);

                    if (await _roleManager.RoleExistsAsync(dto.RoleName))
                        await _userManager.AddToRoleAsync(user, dto.RoleName);
                    else
                        return new Response
                        {
                            Success = false,
                            Message = $"Role '{dto.RoleName}' not found.",
                            MessCode = 0
                        };
                }

                // --- Update claims ---
                if (!string.IsNullOrEmpty(dto.RoleName))
                {
                    var existingClaims = await _userManager.GetClaimsAsync(user);
                    var existingRoleClaim = existingClaims.FirstOrDefault(c => c.Type == "Role");
                    if (existingRoleClaim != null)
                        await _userManager.RemoveClaimAsync(user, existingRoleClaim);

                    await _userManager.AddClaimAsync(user, new Claim("Role", dto.RoleName));
                }

                await transaction.CommitAsync();
                return new Response
                {
                    Success = true,
                    Message = "User updated successfully!",
                    MessCode = 1
                };
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                return new Response
                {
                    Success = false,
                    Message = $"Updating user failed: {ex.Message}",
                    MessCode = 0
                };
            }
        }

        public async Task<List<UserWithRoleDto>> GetUsers(string company)
        {
            try
            {
                var users = await _userManager.Users
                    .Where(u=>u.Company== company)
               .Include(u => u.Department)
               .ToListAsync();

                var roles = await _roleManager.Roles.ToListAsync(); // cache all roles once

                var result = new List<UserWithRoleDto>();

                foreach (var user in users)
                {
                    var roleNames = await _userManager.GetRolesAsync(user);

                    // Map role names to RoleId + RoleName
                    var userRoles = roles
                        .Where(r => roleNames.Contains(r.Name))
                        .Select(r => new RoleDto
                        {
                            RoleId = r.Id,
                            RoleName = r.Name!
                        })
                        .ToList();

                    result.Add(new UserWithRoleDto
                    {
                        Id = user.Id,
                        EmployeeId = user.EmployeeId,
                        Company = user.Company,
                        Address = user.Address,
                        UpdatedDate = user.UpdatedDate,
                        UpdatedBy = user.UpdatedBy,
                        Email = user.Email,
                        FullName = user.FullName,
                        RoleName = userRoles[0].RoleName,
                        CreatedBy = user.CreatedBy,
                        CreatedDate = user.CreatedDate,
                        UserName = user.UserName,
                        DepartmentId = user.DepartmentId,
                        RoleId = userRoles[0].RoleId,
                        Department = user.Department,
                        AccessFailedCount = user.AccessFailedCount,
                        PhoneNumber = user.PhoneNumber,
                        LockoutEnd = user.LockoutEnd,
                        LockoutEnabled = user.LockoutEnabled,
                        UserRoles = userRoles
                    });
                }

                return result;
            }
            catch (Exception ex)
            {
                ex.ToString();
                throw;
            }
           
        }

        public async Task<Response> PostPutDepartmentAsync(Departments dto)
        {
            var existing = await _context.Departments
                .FirstOrDefaultAsync(d => d.DepartmentId == dto.DepartmentId);

            if (existing == null)
            {
                var duplicate = await _context.Departments
                    .AnyAsync(d => d.DepartmentName == dto.DepartmentName 
                    || d.DepartmentCode == dto.DepartmentCode);

                if (duplicate)
                {
                    return new Response
                    {
                        Success = false,
                        Message = "Department name or code already exists.",
                        MessCode = 0
                    };
                }

                _context.Departments.Add(dto);
                await _context.SaveChangesAsync();
                return new Response
                {
                    Success = true,
                    Message = "Department created successfully.",
                    MessCode = 1
                };
            }
            else
            {
                existing.DepartmentName = dto.DepartmentName;
                existing.DepartmentCode = dto.DepartmentCode;
                existing.IsActive = dto.IsActive;

                _context.Departments.Update(existing);
                await _context.SaveChangesAsync();
                return new Response
                {
                    Success = true,
                    Message = "Department updated successfully.",
                    MessCode = 1
                };
            }
        }
        public async Task<List<Departments>> GetDepartment()
        {
            return await _context.Departments.ToListAsync();
        }
        public async Task<List<RoleDto>> GetUserRole()
        {
            return await _roleManager.Roles
                .Select(r => new RoleDto
                {
                    RoleId = r.Id,
                    RoleName = r.Name
                })
                .ToListAsync();
        }
    }
}
