using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using timetracker.Shared;

namespace timetracker.Data;

public class TenantProvider : ITenantProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IDbContextFactory<TimetrackerDbContext> _dbContextFactory;
    private readonly IConfiguration _configuration;

    public TenantProvider(
        IHttpContextAccessor httpContextAccessor,
        IDbContextFactory<TimetrackerDbContext> dbContextFactory,
        IConfiguration configuration)
    {
        _httpContextAccessor = httpContextAccessor;
        _dbContextFactory = dbContextFactory;
        _configuration = configuration;
    }

    public int? TenantId
    {
        get
        {
            var httpContext = _httpContextAccessor.HttpContext;
            if (httpContext == null) return null;

            if (httpContext.Items.TryGetValue("TenantId", out var cachedId))
            {
                return (int?)cachedId;
            }

            // Resolve and cache in HttpContext.Items
            ResolveTenantAsync().GetAwaiter().GetResult();

            if (httpContext.Items.TryGetValue("TenantId", out cachedId))
            {
                return (int?)cachedId;
            }

            return null;
        }
    }

    public async Task<Tenant?> GetCurrentTenantAsync()
    {
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext == null) return null;

        if (httpContext.Items.TryGetValue("CurrentTenant", out var cachedTenant))
        {
            return (Tenant?)cachedTenant;
        }

        await ResolveTenantAsync();

        if (httpContext.Items.TryGetValue("CurrentTenant", out cachedTenant))
        {
            return (Tenant?)cachedTenant;
        }

        return null;
    }

    private async Task ResolveTenantAsync()
    {
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext == null) return;

        // Prevent double resolution in the same request
        if (httpContext.Items.ContainsKey("TenantResolved")) return;
        httpContext.Items["TenantResolved"] = true;

        var host = httpContext.Request.Host.Host;
        var subdomain = GetSubdomain(host);

        if (!string.IsNullOrEmpty(subdomain))
        {
            await using var db = await _dbContextFactory.CreateDbContextAsync();
            var tenant = await db.Tenants
                .FirstOrDefaultAsync(t => t.Subdomain.ToLower() == subdomain.ToLower());

            if (tenant != null)
            {
                httpContext.Items["CurrentTenant"] = tenant;
                httpContext.Items["TenantId"] = tenant.Id;
            }
        }
    }

    private string? GetSubdomain(string host)
    {
        var baseDomain = _configuration["TenantSettings:BaseDomain"] ?? "localhost";

        // Ignore port if included in host
        var hostName = host.Split(':')[0];

        if (hostName.Equals(baseDomain, StringComparison.OrdinalIgnoreCase))
        {
            return null; // Main domain
        }

        if (hostName.EndsWith("." + baseDomain, StringComparison.OrdinalIgnoreCase))
        {
            return hostName.Substring(0, hostName.Length - baseDomain.Length - 1);
        }

        // Fallback for localhost testing
        if (baseDomain.Equals("localhost", StringComparison.OrdinalIgnoreCase))
        {
            var parts = hostName.Split('.');
            if (parts.Length > 1 && !hostName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
            {
                return parts[0];
            }
        }

        return null;
    }
}
