// ==============================================================================
// MORPH-SPEC - Multi-Tenant Model Template
// Modelo de multi-tenancy para SaaS
// ==============================================================================

using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;

namespace {{Namespace}}.Domain.Entities;

// ==============================================================================
// TENANT ENTITY
// ==============================================================================

public class Tenant : BaseEntity
{
    [Required]
    [MaxLength(100)]
    public string Name { get; private set; } = string.Empty;

    [Required]
    [MaxLength(50)]
    public string Slug { get; private set; } = string.Empty; // Unique identifier (URL-friendly)

    [MaxLength(500)]
    public string? Description { get; private set; }

    [MaxLength(200)]
    public string? LogoUrl { get; private set; }

    public TenantStatus Status { get; private set; }

    // Settings
    public string? CustomDomain { get; private set; }
    public string? TimeZone { get; private set; }
    public string? Locale { get; private set; }

    // Billing
    public string? AsaasCustomerId { get; private set; }

    // Navigation
    public ICollection<TenantUser> Users { get; private set; } = new List<TenantUser>();
    public Subscription? CurrentSubscription { get; private set; }

    // =========================================================================
    // FACTORY
    // =========================================================================

    public static Tenant Create(string name, string slug)
    {
        return new Tenant
        {
            Name = name,
            Slug = slug.ToLowerInvariant(),
            Status = TenantStatus.Active,
            TimeZone = "E. South America Standard Time",
            Locale = "pt-BR",
            CreatedAt = DateTime.UtcNow
        };
    }

    // =========================================================================
    // BEHAVIORS
    // =========================================================================

    public void UpdateInfo(string name, string? description, string? logoUrl)
    {
        Name = name;
        Description = description;
        LogoUrl = logoUrl;
        UpdatedAt = DateTime.UtcNow;
    }

    public void SetCustomDomain(string? domain)
    {
        CustomDomain = domain;
        UpdatedAt = DateTime.UtcNow;
    }

    public void SetAsaasCustomerId(string customerId)
    {
        AsaasCustomerId = customerId;
        UpdatedAt = DateTime.UtcNow;
    }

    public void Suspend()
    {
        Status = TenantStatus.Suspended;
        UpdatedAt = DateTime.UtcNow;
    }

    public void Activate()
    {
        Status = TenantStatus.Active;
        UpdatedAt = DateTime.UtcNow;
    }

    public void Delete()
    {
        Status = TenantStatus.Deleted;
        UpdatedAt = DateTime.UtcNow;
    }

    // =========================================================================
    // QUERIES
    // =========================================================================

    public bool IsActive => Status == TenantStatus.Active;
    public bool IsSuspended => Status == TenantStatus.Suspended;
    public bool IsDeleted => Status == TenantStatus.Deleted;
}

// ==============================================================================
// TENANT USER ENTITY (Association between Users and Tenants)
// ==============================================================================

public class TenantUser : BaseEntity
{
    public int TenantId { get; private set; }
    public Tenant Tenant { get; private set; } = null!;

    [Required]
    [MaxLength(100)]
    public string ExternalUserId { get; private set; } = string.Empty; // Clerk/Azure AD user ID

    [Required]
    [MaxLength(200)]
    public string Email { get; private set; } = string.Empty;

    [MaxLength(100)]
    public string? Name { get; private set; }

    public TenantUserRole Role { get; private set; }
    public TenantUserStatus Status { get; private set; }

    public DateTime? LastLoginAt { get; private set; }

    // =========================================================================
    // FACTORY
    // =========================================================================

    public static TenantUser Create(
        int tenantId,
        string externalUserId,
        string email,
        string? name,
        TenantUserRole role = TenantUserRole.Member)
    {
        return new TenantUser
        {
            TenantId = tenantId,
            ExternalUserId = externalUserId,
            Email = email,
            Name = name,
            Role = role,
            Status = TenantUserStatus.Active,
            CreatedAt = DateTime.UtcNow
        };
    }

    // =========================================================================
    // BEHAVIORS
    // =========================================================================

    public void UpdateRole(TenantUserRole role)
    {
        Role = role;
        UpdatedAt = DateTime.UtcNow;
    }

    public void RecordLogin()
    {
        LastLoginAt = DateTime.UtcNow;
    }

    public void Deactivate()
    {
        Status = TenantUserStatus.Inactive;
        UpdatedAt = DateTime.UtcNow;
    }

    public void Activate()
    {
        Status = TenantUserStatus.Active;
        UpdatedAt = DateTime.UtcNow;
    }

    // =========================================================================
    // QUERIES
    // =========================================================================

    public bool IsOwner => Role == TenantUserRole.Owner;
    public bool IsAdmin => Role == TenantUserRole.Owner || Role == TenantUserRole.Admin;
    public bool IsActive => Status == TenantUserStatus.Active;
}

// ==============================================================================
// ENUMS
// ==============================================================================

public enum TenantStatus
{
    Active,
    Suspended,
    Deleted
}

public enum TenantUserRole
{
    Owner,
    Admin,
    Member,
    Viewer
}

public enum TenantUserStatus
{
    Pending,
    Active,
    Inactive
}

// ==============================================================================
// TENANT CONTEXT (Current Tenant Resolution)
// ==============================================================================

public interface ITenantContext
{
    int? TenantId { get; }
    Tenant? CurrentTenant { get; }
    Task<Tenant?> GetCurrentTenantAsync(CancellationToken ct = default);
}

public class TenantContext : ITenantContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ITenantRepository _tenantRepository;
    private Tenant? _currentTenant;
    private bool _loaded;

    public TenantContext(
        IHttpContextAccessor httpContextAccessor,
        ITenantRepository tenantRepository)
    {
        _httpContextAccessor = httpContextAccessor;
        _tenantRepository = tenantRepository;
    }

    public int? TenantId => _currentTenant?.Id;
    public Tenant? CurrentTenant => _currentTenant;

    public async Task<Tenant?> GetCurrentTenantAsync(CancellationToken ct = default)
    {
        if (_loaded) return _currentTenant;

        var tenantSlug = ResolveTenantSlug();
        if (string.IsNullOrEmpty(tenantSlug))
        {
            _loaded = true;
            return null;
        }

        _currentTenant = await _tenantRepository.GetBySlugAsync(tenantSlug, ct);
        _loaded = true;

        return _currentTenant;
    }

    private string? ResolveTenantSlug()
    {
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext is null) return null;

        // Strategy 1: From route (e.g., /tenant/{slug}/...)
        if (httpContext.Request.RouteValues.TryGetValue("tenantSlug", out var routeSlug))
            return routeSlug?.ToString();

        // Strategy 2: From header (e.g., X-Tenant-ID)
        if (httpContext.Request.Headers.TryGetValue("X-Tenant-ID", out var headerSlug))
            return headerSlug.FirstOrDefault();

        // Strategy 3: From subdomain (e.g., acme.app.com)
        var host = httpContext.Request.Host.Host;
        var parts = host.Split('.');
        if (parts.Length >= 3)
            return parts[0];

        // Strategy 4: From claim
        var claimSlug = httpContext.User.FindFirst("tenant_id")?.Value;
        if (!string.IsNullOrEmpty(claimSlug))
            return claimSlug;

        return null;
    }
}

// ==============================================================================
// TENANT REPOSITORY INTERFACE
// ==============================================================================

public interface ITenantRepository
{
    Task<Tenant?> GetByIdAsync(int id, CancellationToken ct = default);
    Task<Tenant?> GetBySlugAsync(string slug, CancellationToken ct = default);
    Task<bool> SlugExistsAsync(string slug, CancellationToken ct = default);
    Task AddAsync(Tenant tenant, CancellationToken ct = default);
    void Update(Tenant tenant);
}

// ==============================================================================
// TENANT QUERY FILTER (EF Core)
// ==============================================================================

/*
// In DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // Apply tenant filter to all tenant-scoped entities
    modelBuilder.Entity<Order>().HasQueryFilter(o => o.TenantId == _tenantContext.TenantId);
    modelBuilder.Entity<Customer>().HasQueryFilter(c => c.TenantId == _tenantContext.TenantId);
    // ... other entities
}
*/

// ==============================================================================
// DEPENDENCY INJECTION
// ==============================================================================

public static class TenantServiceExtensions
{
    public static IServiceCollection AddMultiTenancy(this IServiceCollection services)
    {
        services.AddScoped<ITenantContext, TenantContext>();
        services.AddScoped<ITenantRepository, TenantRepository>();

        return services;
    }
}
