using Fengling.Console.Models.Dtos;
using Fengling.Console.Repositories;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;

namespace Fengling.Console.Services;

public interface ITenantService
{
    Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync(int page, int pageSize, string? name = null,
        string? tenantCode = null, TenantStatus? status = null);
    Task<TenantDto?> GetTenantAsync(long id);
    Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId);
    Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId);
    Task<TenantSettingsDto> GetTenantSettingsAsync(long id);
    Task UpdateTenantSettingsAsync(long id, TenantSettingsDto settings);
    Task<TenantDto> CreateTenantAsync(CreateTenantDto dto);
    Task<TenantDto> UpdateTenantAsync(long id, UpdateTenantDto dto);
    Task DeleteTenantAsync(long id);
}

public class TenantService(
    ITenantRepository repository,
    IUserRepository userRepository,
    IRoleRepository roleRepository,
    UserManager<ApplicationUser> userManager,
    ApplicationDbContext context,
    IHttpContextAccessor httpContextAccessor)
    : ITenantService
{
    public async Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync
        (int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null)
    {
        var tenants = await repository.GetPagedAsync(page, pageSize, name, tenantCode, status);
        var totalCount = await repository.CountAsync(name, tenantCode, status);

        var tenantDtos = new List<TenantDto>();
        foreach (var tenant in tenants)
        {
            var userCount = await repository.GetUserCountAsync(tenant.Id);
            tenantDtos.Add(new TenantDto
            {
                Id = tenant.Id,
                TenantCode = tenant.TenantCode,
                Name = tenant.Name,
                ContactName = tenant.ContactName,
                ContactEmail = tenant.ContactEmail,
                ContactPhone = tenant.ContactPhone,
                MaxUsers = tenant.MaxUsers,
                UserCount = userCount,
                Status = tenant.Status,
                ExpiresAt = tenant.ExpiresAt,
                Description = tenant.Description,
                CreatedAt = tenant.CreatedAt
            });
        }

        return (tenantDtos, totalCount);
    }

    public async Task<TenantDto?> GetTenantAsync(long id)
    {
        var tenant = await repository.GetByIdAsync(id);
        if (tenant == null) return null;

        return new TenantDto
        {
            Id = tenant.Id,
            TenantCode = tenant.TenantCode,
            Name = tenant.Name,
            ContactName = tenant.ContactName,
            ContactEmail = tenant.ContactEmail,
            ContactPhone = tenant.ContactPhone,
            MaxUsers = tenant.MaxUsers,
            UserCount = await repository.GetUserCountAsync(tenant.Id),
            Status = tenant.Status,
            ExpiresAt = tenant.ExpiresAt,
            Description = tenant.Description,
            CreatedAt = tenant.CreatedAt
        };
    }

    public async Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId)
    {
        var tenant = await repository.GetByIdAsync(tenantId);
        if (tenant == null)
        {
            throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
        }

        var users = await userRepository.GetPagedAsync(1, int.MaxValue, null, null, tenantId.ToString());
        var userDtos = new List<UserDto>();

        foreach (var user in users)
        {
            var roles = await userManager.GetRolesAsync(user);
            userDtos.Add(new UserDto
            {
                Id = user.Id,
                UserName = user.UserName,
                Email = user.Email,
                RealName = user.RealName,
                TenantCode = user.TenantInfo.TenantCode,
                TenantName = user.TenantInfo.TenantName,
                Roles = roles.ToList(),
                EmailConfirmed = user.EmailConfirmed,
                IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
                CreatedAt = user.CreatedTime
            });
        }

        return userDtos;
    }

    public async Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId)
    {
        var tenant = await repository.GetByIdAsync(tenantId);
        if (tenant == null)
        {
            throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
        }

        var roles = await roleRepository.GetPagedAsync(1, int.MaxValue, null, tenantId.ToString());
        return roles.Select(r => new
        {
            id = r.Id,
            name = r.Name,
            displayName = r.DisplayName
        });
    }

    public async Task<TenantSettingsDto> GetTenantSettingsAsync(long id)
    {
        var tenant = await repository.GetByIdAsync(id);
        if (tenant == null)
        {
            throw new KeyNotFoundException($"Tenant with ID {id} not found");
        }

        return new TenantSettingsDto
        {
            AllowRegistration = false,
            AllowedEmailDomains = "",
            DefaultRoleId = null,
            PasswordPolicy = new List<string> { "requireNumber", "requireLowercase" },
            MinPasswordLength = 8,
            SessionTimeout = 120
        };
    }

    public async Task UpdateTenantSettingsAsync(long id, TenantSettingsDto settings)
    {
        var tenant = await repository.GetByIdAsync(id);
        if (tenant == null)
        {
            throw new KeyNotFoundException($"Tenant with ID {id} not found");
        }

        await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.Name, null, System.Text.Json.JsonSerializer.Serialize(settings));
    }

    public async Task<TenantDto> CreateTenantAsync(CreateTenantDto dto)
    {
        var tenant = new Tenant(dto.TenantCode, dto.Name, dto.ContactName, dto.ContactEmail, 
            dto.ContactPhone,dto.MaxUsers,dto.Description,dto.ExpiresAt);

        await repository.AddAsync(tenant);

        await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.Name, null, 
            System.Text.Json.JsonSerializer.Serialize(dto));

        return new TenantDto
        {
            Id = tenant.Id,
            TenantCode = tenant.TenantCode,
            Name = tenant.Name,
            ContactName = tenant.ContactName,
            ContactEmail = tenant.ContactEmail,
            ContactPhone = tenant.ContactPhone,
            MaxUsers = tenant.MaxUsers,
            UserCount = 0,
            Status = tenant.Status,
            ExpiresAt = tenant.ExpiresAt,
            Description = tenant.Description,
            CreatedAt = tenant.CreatedAt
        };
    }

    public async Task<TenantDto> UpdateTenantAsync(long id, UpdateTenantDto dto)
    {
        var tenant = await repository.GetByIdAsync(id);
        if (tenant == null)
        {
            throw new KeyNotFoundException($"Tenant with ID {id} not found");
        }

        var oldValue = System.Text.Json.JsonSerializer.Serialize(tenant);

        tenant.UpdateInfo(dto.Name,dto.ContactName,dto.ContactEmail,dto.ContactPhone);

        await repository.UpdateAsync(tenant);

        await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.Name, oldValue, 
            System.Text.Json.JsonSerializer.Serialize(tenant));

        return new TenantDto
        {
            Id = tenant.Id,
            TenantCode = tenant.TenantCode,
            Name = tenant.Name,
            ContactName = tenant.ContactName,
            ContactEmail = tenant.ContactEmail,
            ContactPhone = tenant.ContactPhone,
            MaxUsers = tenant.MaxUsers,
            UserCount = await repository.GetUserCountAsync(tenant.Id),
            Status = tenant.Status,
            ExpiresAt = tenant.ExpiresAt,
            Description = tenant.Description,
            CreatedAt = tenant.CreatedAt
        };
    }

    public async Task DeleteTenantAsync(long id)
    {
        var tenant = await repository.GetByIdAsync(id);
        if (tenant == null)
        {
            throw new KeyNotFoundException($"Tenant with ID {id} not found");
        }

        var oldValue = System.Text.Json.JsonSerializer.Serialize(tenant);

        var users = await userRepository.GetPagedAsync(1, int.MaxValue, null, null, id.ToString());
        foreach (var user in users)
        {
            user.IsDeleted = true;
            user.UpdatedTime = DateTime.UtcNow;
            await context.SaveChangesAsync();
        }

        tenant.Delete();;
        await repository.UpdateAsync(tenant);

        await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.Name, oldValue);
    }

    private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
    {
        var httpContext = httpContextAccessor.HttpContext;
        var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? httpContext?.User?.Identity?.Name ?? "system";
        var tenantId = httpContext?.User?.FindFirstValue("TenantId");

        var log = new AuditLog
        {
            Operator = userName,
            TenantId = tenantId,
            Operation = operation,
            Action = action,
            TargetType = targetType,
            TargetId = targetId,
            TargetName = targetName,
            IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString() ?? "unknown",
            Status = "success",
            OldValue = oldValue,
            NewValue = newValue,
            CreatedAt = DateTime.UtcNow
        };

        context.AuditLogs.Add(log);
        await context.SaveChangesAsync();
    }
}
