module DocsXRefTests

# Testing the fallback behavior implemented in
# https://github.com/JuliaDocs/Documenter.jl/pull/2470

using Test
using Documenter
using IOCapture

isdefined(Main, :Documenter) || @eval Main import Documenter

isdefined(Main, :AbstractSelector) || @eval Main using Documenter.Selectors: AbstractSelector

isdefined(Main, :DocsReferencingMain) || @eval Main module DocsReferencingMain
    export f, g

    """This is the function `f`.

    It references [`Documenter.Selectors.AbstractSelector`](@ref), which
    resolves only because of a fallback to `Main`.

    See also [`g`](@ref).
    """
    f(x) = x

    """This is the function `g`

    If references [`Main.AbstractSelector`](@ref), which should resolve,
    unlike the non-fully-qualified [`AbstractSelector`](@ref) (even though
    `AbstractSelector` is in `Main`)

    See also [`f`](@ref).
    """
    g(x) = x
end


@testset "xrefs to Main" begin

    kwargs = (
        root = dirname(@__FILE__),
        source = "src",
        build = "build",
        sitename = "DocsXRef",
        warnonly = true,
        format = Documenter.HTML(
            prettyurls = false,
            inventory_version = "",
        ),
    )

    captured = IOCapture.capture() do
        makedocs(; kwargs...)
    end

    output = captured.output
    output = replace(output, "\\src\\index" => "/src/index")
    output = replace(output, "\\src\\page" => "/src/page")
    output = replace(output, "test/docsxref/src/index" => "docsxref/src/index")
    output = replace(output, "test/docsxref/src/page" => "docsxref/src/page")

    @test isnothing(captured.value)
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[`AbstractSelector`](@ref)" in docsxref/src/index.md.
        │ - No docstring found in doc for binding `Main.DocsReferencingMain.AbstractSelector`.
        │ - Fallback resolution in Main for `AbstractSelector` -> `Documenter.Selectors.AbstractSelector` is only allowed for fully qualified names
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[`DocsReferencingMain.f`](@ref)" in docsxref/src/page.md.
        │ - Exception trying to find docref for `DocsReferencingMain.f`: unable to get the binding for `DocsReferencingMain.f` in module Documenter.Selectors
        │ - Fallback resolution in Main for `DocsReferencingMain.f` -> `Main.DocsReferencingMain.f` is only allowed for fully qualified names
        """
    )

    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[header](@ref)" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'header' in docsxref/src/index.md does not exist.
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[header link](@ref \\\"header\\\")" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'header' in docsxref/src/index.md does not exist.
        """
    )

    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[Multiple words](@ref)" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'Multiple-words' in docsxref/src/index.md does not exist.
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[header link](@ref \\\"Multiple words\\\")" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'Multiple-words' in docsxref/src/index.md does not exist.
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[header id link](@ref missing-header-id)" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'missing-header-id' in docsxref/src/index.md does not exist.
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[dashed header id link](@ref missing-header-id-with-dashes)" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'missing-header-id-with-dashes' in docsxref/src/index.md does not exist.
        """
    )

    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[`foobar`](@ref)" in docsxref/src/index.md.
        │ - No docstring found in doc for binding `Main.foobar`.
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[docstring link](@ref Main.foobar)" in docsxref/src/index.md.
        │ - No docstring found in doc for binding `Main.foobar`.
        """
    )
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[docstring code link](@ref `Main.foobar`)" in docsxref/src/index.md.
        │ - No docstring found in doc for binding `Main.foobar`.
        """
    )

    # An implicit target that is neither a header nor a plausible binding is
    # reported, not silently resolved to the docstring of `-` (#2960).
    @test contains(
        output,
        """
        ┌ Warning: Cannot resolve @ref for md"[Some-Hyphenated-Target](@ref)" in docsxref/src/index.md.
        │ - Header or `@id` anchor with slug 'Some-Hyphenated-Target' in docsxref/src/index.md does not exist.
        """
    )
    @test !contains(output, "binding `Base.-`")

    index_html = joinpath(dirname(@__FILE__), "build", "index.html")
    @test isfile(index_html)
    if isfile(index_html)
        html = read(index_html, String)
        @test contains(html, "<a href=\"index.html#Documenter.Selectors.AbstractSelector\"><code>AbstractSelector</code></a>")
        @test contains(html, "<a href=\"index.html#Documenter.Selectors.AbstractSelector\"><code>Documenter.Selectors.AbstractSelector</code></a>")
        @test contains(html, "<a href=\"index.html#Documenter.Selectors.AbstractSelector\"><code>Main.AbstractSelector</code></a>")
        @test contains(html, "<a href=\"@ref\"><code>AbstractSelector</code></a>")

        @test contains(html, "<a href=\"index.html#API\">API</a>")
        @test contains(html, "<a href=\"index.html#API\">header link</a>")
        @test contains(html, "<a href=\"index.html#Two-words\">Two words</a>")
        @test contains(html, "<a href=\"index.html#Two-words\">header link</a>")
        @test contains(html, "<a href=\"index.html#api-reference\">header id link</a>")
        @test contains(html, "<a href=\"index.html#multi-word-section-id\">multi-dash id link</a>")
        @test contains(html, "<a href=\"https://github.com/JuliaDocs/Documenter.jl/issues/12345\">#12345</a>")
        @test contains(html, "<a href=\"https://github.com/JuliaDocs/Documenter.jl/issues/12345\">issue link</a>")
        @test contains(html, "<a href=\"index.html#Main.DocsReferencingMain.g\"><code>DocsReferencingMain.g</code></a>")
        @test contains(html, "<a href=\"index.html#Main.DocsReferencingMain.f\">docstring link</a>")
        @test contains(html, "<a href=\"index.html#Main.DocsReferencingMain.g\">docstring code link</a>")
        @test contains(html, "<a href=\"index.html#DocsReferencingMain.g\">conflict link</a>")
        # Plain text is a header reference only, so this names nothing (#2960).
        @test contains(html, "<a href=\"@ref\">DocsReferencingMain.f</a>")
        # Precedence by syntax: plain text prefers the header, backticks the docstring.
        @test contains(html, "<a href=\"index.html#DocsReferencingMain.g\">DocsReferencingMain.g</a>")
        @test contains(html, "<a href=\"index.html#Main.DocsReferencingMain.g\"><code>DocsReferencingMain.g</code></a>")
        # Backticked link text still finds a header `@id` (#2960).
        @test contains(html, "<a href=\"index.html#ENVVAR\"><code>ENVVAR</code></a>")
        @test contains(html, "<a href=\"index.html#Main.DocsReferencingMain.g\">conflict docs link</a>")
    end
    page_html = joinpath(dirname(@__FILE__), "build", "page.html")
    @test isfile(page_html)
    if isfile(page_html)
        html = read(page_html, String)
        @test contains(html, "<a href=\"index.html#Documenter.Selectors.AbstractSelector\"><code>AbstractSelector</code></a>")
        @test contains(html, "<a href=\"@ref\"><code>DocsReferencingMain.f</code></a>")
        @test contains(html, "<a href=\"index.html#Main.DocsReferencingMain.f\"><code>Main.DocsReferencingMain.f</code></a>")
        @test contains(html, "<a href=\"index.html#Documenter.hide\"><code>Documenter.hide</code></a>")
    end


end

end
