using Books.Api.Authorization;
using Books.Api.Domain.UserAccess;
using GraphQL;

namespace Books.Api.GraphQL;

/// <summary>
/// Extension methods for accessing CompanyContext from GraphQL resolvers.
/// </summary>
public static class GraphQLContextExtensions
{
    /// <summary>
    /// Get the CompanyContext from the GraphQL resolver context.
    /// </summary>
    public static CompanyContext GetCompanyContext(this IResolveFieldContext ctx)
    {
        var httpContext = ctx.RequestServices?.GetService<IHttpContextAccessor>()?.HttpContext;
        return httpContext?.GetCompanyContext() ?? new CompanyContext();
    }

    /// <summary>
    /// Require the user to have at least the specified role for the selected company.
    /// Throws DomainException if not.
    /// </summary>
    public static void RequireRole(this IResolveFieldContext ctx, CompanyRole minimumRole)
    {
        ctx.GetCompanyContext().RequireRole(minimumRole);
    }

    /// <summary>
    /// Require the user to have write access (Owner or Accountant).
    /// </summary>
    public static void RequireWrite(this IResolveFieldContext ctx)
    {
        ctx.GetCompanyContext().RequireWrite();
    }

    /// <summary>
    /// Require the user to be Owner.
    /// </summary>
    public static void RequireOwner(this IResolveFieldContext ctx)
    {
        ctx.GetCompanyContext().RequireOwner();
    }

    /// <summary>
    /// Get the currently selected company ID.
    /// Throws if no company is selected.
    /// </summary>
    public static string GetRequiredCompanyId(this IResolveFieldContext ctx)
    {
        var companyContext = ctx.GetCompanyContext();
        if (!companyContext.HasCompanySelected)
        {
            throw new Domain.DomainException(
                "NO_COMPANY_SELECTED",
                "No company selected. Set X-Company-Id header.",
                "Ingen virksomhed valgt. Sæt X-Company-Id header.");
        }
        return companyContext.CompanyId!;
    }

    /// <summary>
    /// Validate that the requested company ID matches the selected company.
    /// For operations that take a company ID argument.
    /// </summary>
    public static void ValidateCompanyAccess(this IResolveFieldContext ctx, string requestedCompanyId)
    {
        var companyContext = ctx.GetCompanyContext();
        if (!companyContext.HasCompanySelected)
        {
            throw new Domain.DomainException(
                "NO_COMPANY_SELECTED",
                "No company selected. Set X-Company-Id header.",
                "Ingen virksomhed valgt. Sæt X-Company-Id header.");
        }

        if (companyContext.CompanyId != requestedCompanyId)
        {
            throw new Domain.DomainException(
                "COMPANY_MISMATCH",
                "The requested company ID does not match the selected company",
                "Det angivne virksomheds-ID matcher ikke den valgte virksomhed");
        }
    }
}
