using Fengling.AuthService.Data;
using Fengling.AuthService.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using System.Text.Json;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;

namespace Fengling.AuthService.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class TenantsController(
    ApplicationDbContext context,
    UserManager<ApplicationUser> userManager,
    ILogger<TenantsController> logger,
    PlatformDbContext platformDbContext)
    : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<object>> GetTenants(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 10,
        [FromQuery] string? name = null,
        [FromQuery] string? tenantCode = null,
        [FromQuery] TenantStatus? status = null)
    {
        var query = platformDbContext.Tenants.AsQueryable();

        if (!string.IsNullOrEmpty(name))
        {
            query = query.Where(t => t.Name.Contains(name));
        }

        if (!string.IsNullOrEmpty(tenantCode))
        {
            query = query.Where(t => t.TenantCode.Contains(tenantCode));
        }

        if (status.HasValue)
        {
            query = query.Where(t => t.Status == status);
        }

        var totalCount = await query.CountAsync();
        var tenants = await query
            .OrderByDescending(t => t.CreatedAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        var result = new List<object>();

        foreach (var tenant in tenants)
        {
            var userCount = await context.Users.CountAsync(u => u.TenantInfo.TenantId == tenant.Id && !u.IsDeleted);
            result.Add(new
            {
                id = tenant.Id,
                tenantId = tenant.Id,
                name = tenant.Name,
                contactName = tenant.ContactName,
                contactEmail = tenant.ContactEmail,
                contactPhone = tenant.ContactPhone,
                maxUsers = tenant.MaxUsers,
                userCount,
                status = tenant.Status,
                expiresAt = tenant.ExpiresAt,
                description = tenant.Description,
                createdAt = tenant.CreatedAt,
            });
        }

        return Ok(new
        {
            items = result,
            totalCount,
            page,
            pageSize
        });
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Tenant>> GetTenant(long id)
    {
        var tenant = await platformDbContext.Tenants.FindAsync(id);
        if (tenant == null)
        {
            return NotFound();
        }

        return Ok(new
        {
            id = tenant.Id,
            tenantId = tenant.Id,
            name = tenant.Name,
            contactName = tenant.ContactName,
            contactEmail = tenant.ContactEmail,
            contactPhone = tenant.ContactPhone,
            maxUsers = tenant.MaxUsers,
            status = tenant.Status,
            expiresAt = tenant.ExpiresAt,
            description = tenant.Description,
            createdAt = tenant.CreatedAt,
            updatedAt = tenant.UpdatedAt,
        });
    }

    [HttpGet("{tenantId}/users")]
    public async Task<ActionResult<List<object>>> GetTenantUsers(long tenantId)
    {
        var tenant = await platformDbContext.Tenants
            .FirstOrDefaultAsync(t => t.Id == tenantId);
        if (tenant == null)
        {
            return NotFound();
        }

        var users = await context.Users
            .Where(u =>
                u.TenantInfo.TenantId == tenant.Id
                && !u.IsDeleted)
            .ToListAsync();

        var result = users.Select(async u => new
        {
            id = u.Id,
            userName = u.UserName,
            email = u.Email,
            realName = u.RealName,
            tenantId = u.TenantInfo.TenantId,
            roles = await userManager.GetRolesAsync(u),
            isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
            createdAt = u.CreatedTime,
        });

        return Ok(await Task.WhenAll(result));
    }

    [HttpGet("{tenantId}/roles")]
    public async Task<ActionResult<List<object>>> GetTenantRoles(long tenantId)
    {
        var tenant = await platformDbContext.Tenants
            .FirstOrDefaultAsync(t => t.Id == tenantId);
        if (tenant == null)
        {
            return NotFound();
        }

        var roles = await context.Roles
            .Where(r => r.TenantId == tenant.Id)
            .ToListAsync();

        var result = roles.Select(r => new
        {
            id = r.Id,
            name = r.Name,
            displayName = r.DisplayName,
        });

        return Ok(result);
    }

    [HttpGet("{tenantId}/settings")]
    public async Task<ActionResult<TenantSettings>> GetTenantSettings(long tenantId)
    {
        var tenant = await platformDbContext.Tenants
            .FirstOrDefaultAsync(t => t.Id == tenantId);
        if (tenant == null)
        {
            return NotFound();
        }

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

        return Ok(settings);
    }

    [HttpPut("{tenantId}/settings")]
    public async Task<IActionResult> UpdateTenantSettings(long tenantId, TenantSettings settings)
    {
        var tenant = await platformDbContext.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId);
        if (tenant == null)
        {
            return NotFound();
        }

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

        return NoContent();
    }

    [HttpPost]
    public async Task<ActionResult<Tenant>> CreateTenant(CreateTenantDto dto)
    {
        var tenant = new Tenant
        {
            TenantCode = dto.TenantCode,
            Name = dto.TenantName,
            ContactName = dto.ContactName,
            ContactEmail = dto.ContactEmail,
            ContactPhone = dto.ContactPhone,
            MaxUsers = dto.MaxUsers,
            Description = dto.Description,
            ExpiresAt = dto.ExpiresAt,
            Status = TenantStatus.Active,
            CreatedAt = DateTime.UtcNow,
        };

        platformDbContext.Tenants.Add(tenant);
        await context.SaveChangesAsync();

        await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantCode,
            null, JsonSerializer.Serialize(dto));

        return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateTenant(long id, UpdateTenantDto dto)
    {
        var tenant = await platformDbContext.Tenants.FindAsync(id);
        if (tenant == null)
        {
            return NotFound();
        }

        var oldValue = JsonSerializer.Serialize(tenant);
        tenant.Name = dto.Name;
        tenant.ContactName = dto.ContactName;
        tenant.ContactEmail = dto.ContactEmail;
        tenant.ContactPhone = dto.ContactPhone;
        tenant.UpdatedAt = DateTime.UtcNow;


        await context.SaveChangesAsync();

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

        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTenant(long id)
    {
        var tenant = await platformDbContext.Tenants.FindAsync(id);
        if (tenant == null)
        {
            return NotFound();
        }

        var oldValue = JsonSerializer.Serialize(tenant);

        var users = await context.Users.Where(u => u.TenantInfo.TenantId == tenant.Id).ToListAsync();
        foreach (var user in users)
        {
            user.IsDeleted = true;
            user.UpdatedTime = DateTime.UtcNow;
        }

        tenant.IsDeleted = true;
        tenant.UpdatedAt = DateTime.UtcNow;
        await context.SaveChangesAsync();

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

        return NoContent();
    }

    private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId,
        string? targetName, string? oldValue = null, string? newValue = null)
    {
        var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system";
        var tenantId = 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,
        };

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

public record CreateTenantDto(
    string TenantCode,
    string TenantName,
    string ContactName,
    string ContactEmail,
    string ContactPhone,
    int? MaxUsers,
    string? Description,
    string Status,
    DateTime? ExpiresAt);

public class UpdateTenantDto
{
    public string Name { get; set; } = string.Empty;
    public string ContactName { get; set; } = string.Empty;
    public string ContactEmail { get; set; } = string.Empty;
    public string? ContactPhone { get; set; }
    public int? MaxUsers { get; set; }
    public string? Description { get; set; }
    public string Status { get; set; } = "active";
    public DateTime? ExpiresAt { get; set; }
}

public class TenantSettings
{
    public bool AllowRegistration { get; set; }
    public string AllowedEmailDomains { get; set; } = string.Empty;
    public long? DefaultRoleId { get; set; }
    public List<string> PasswordPolicy { get; set; } = new();
    public int MinPasswordLength { get; set; } = 8;
    public int SessionTimeout { get; set; } = 120;
}