namespace Tiku.Application.Security;

public enum TenantResolutionSource
{
    None,
    Host,
    TenantCode,
    Jwt,
    RefreshToken,
    Worker,
    System
}

public interface ITenantContext
{
    Guid? TenantId { get; }
    string? TenantCode { get; }
    TenantResolutionSource ResolutionSource { get; }
    bool IsResolved { get; }
    bool IsSystem { get; }
}

public interface ITenantContextInitializer
{
    void Initialize(Guid tenantId, string? tenantCode, TenantResolutionSource source);
    void InitializeSystem(Guid? targetTenantId, string reason);
}

public sealed class TenantContext : ITenantContext, ITenantContextInitializer
{
    public Guid? TenantId { get; private set; }
    public string? TenantCode { get; private set; }
    public TenantResolutionSource ResolutionSource { get; private set; }
    public bool IsResolved => TenantId.HasValue;
    public bool IsSystem { get; private set; }
    internal string? SystemReason { get; private set; }

    public void Initialize(Guid tenantId, string? tenantCode, TenantResolutionSource source)
    {
        if (tenantId == Guid.Empty)
        {
            throw new ArgumentException("Tenant ID cannot be empty.", nameof(tenantId));
        }

        if (IsSystem)
        {
            throw new InvalidOperationException("A system tenant context cannot be replaced.");
        }

        if (TenantId.HasValue && TenantId.Value != tenantId)
        {
            throw new TenantContextConflictException(TenantId.Value, tenantId);
        }

        var wasResolved = TenantId.HasValue;
        TenantId = tenantId;
        TenantCode = string.IsNullOrWhiteSpace(tenantCode) ? TenantCode : tenantCode.Trim();
        if (!wasResolved)
        {
            ResolutionSource = source;
        }
    }

    public void InitializeSystem(Guid? targetTenantId, string reason)
    {
        if (IsResolved || IsSystem)
        {
            throw new InvalidOperationException("Tenant context has already been initialized.");
        }

        if (string.IsNullOrWhiteSpace(reason))
        {
            throw new ArgumentException("A system scope requires an audit reason.", nameof(reason));
        }

        TenantId = targetTenantId;
        ResolutionSource = TenantResolutionSource.System;
        IsSystem = true;
        SystemReason = reason.Trim();
    }
}

public sealed class TenantContextConflictException(Guid expectedTenantId, Guid actualTenantId)
    : Exception($"Resolved tenant '{expectedTenantId}' conflicts with authenticated tenant '{actualTenantId}'.")
{
    public Guid ExpectedTenantId { get; } = expectedTenantId;
    public Guid ActualTenantId { get; } = actualTenantId;
}
