module IntegralsHAdaptiveIntegrationExt

using Integrals
using HAdaptiveIntegration: HAdaptiveIntegration
using HAdaptiveIntegration.Domain: AbstractDomain, Cuboid, Orthotope, Rectangle, Segment

using Integrals: HAdaptiveIntegrationJL

using SciMLLogging: @SciMLMessage

function _domain_from_bounds(lb::Number, ub::Number)
    return Segment(lb, ub)
end

function _domain_from_bounds(lb::AbstractVector, ub::AbstractVector)
    d = length(lb)
    if d == 1
        return Segment(lb[1], ub[1])
    elseif d == 2
        return Rectangle(Tuple(lb), Tuple(ub))
    elseif d == 3
        return Cuboid(Tuple(lb), Tuple(ub))
    else
        return Orthotope(Tuple(lb), Tuple(ub))
    end
end

function _domain_from_bounds(lb::Tuple, ub::Tuple)
    d = length(lb)
    if d == 1
        return Segment(lb[1], ub[1])
    elseif d == 2
        return Rectangle(lb, ub)
    elseif d == 3
        return Cuboid(lb, ub)
    else
        return Orthotope(lb, ub)
    end
end

function Integrals.__solvebp_call(
        cache::Integrals.IntegralCache, alg::HAdaptiveIntegrationJL, sensealg, domain, p;
        reltol = 1.0e-8, abstol = 1.0e-8,
        maxiters = typemax(Int)
    )
    prob = Integrals.build_problem(cache)
    f = cache.f

    @SciMLMessage(
        lazy"HAdaptiveIntegrationJL: starting h-adaptive integration with reltol=$reltol, abstol=$abstol, maxsubdiv=$maxiters",
        cache.verbosity, :algorithm_selection
    )

    @assert f isa IntegralFunction "HAdaptiveIntegrationJL does not support BatchIntegralFunction"
    @assert !isinplace(f) "HAdaptiveIntegrationJL does not support in-place integrands"

    # Determine the HAdaptiveIntegration domain
    hadaptive_domain = if domain isa AbstractDomain
        domain
    else
        lb, ub = domain
        _domain_from_bounds(lb, ub)
    end

    @SciMLMessage(
        lazy"Constructed $(typeof(hadaptive_domain).name.name) domain from bounds",
        cache.verbosity, :domain_transformation
    )

    _f = if hadaptive_domain isa Segment
        x -> f(x[1], p)
    else
        x -> f(x, p)
    end

    val, err = HAdaptiveIntegration.integrate(
        _f, hadaptive_domain;
        atol = abstol, rtol = reltol, maxsubdiv = maxiters,
        alg.kws...
    )

    @SciMLMessage(
        lazy"HAdaptiveIntegrationJL converged: val=$val, err=$err",
        cache.verbosity, :convergence_result
    )

    return SciMLBase.build_solution(prob, alg, val, err, retcode = ReturnCode.Success)
end

end
