# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import pathlib
import subprocess
from typing import Any, Dict

from sphinx.ext.doctest import (DocTestBuilder, TestcodeDirective,
                                TestoutputDirective, doctest, sphinx)
from sphinx.locale import __


class JavaTestcodeDirective(TestcodeDirective):
    def run(self):
        node_list = super().run()
        node_list[0]["language"] = "java"
        return node_list


class JavaDocTestBuilder(DocTestBuilder):
    """
    Runs java test snippets in the documentation.
    """

    name = "javadoctest"
    epilog = __(
        "Java testing of doctests in the sources finished, look at the "
        "results in %(outdir)s/output.txt."
    )

    def compile(
        self, code: str, name: str, type: str, flags: Any, dont_inherit: bool
    ) -> Any:
        # go to project that contains all your arrow maven dependencies
        path_arrow_project = pathlib.Path(__file__).parent.parent / "source" / "demo"
        # create list of all arrow jar dependencies
        subprocess.check_call(
            [
                "mvn",
                "-q",
                "dependency:build-classpath",
                "-DincludeTypes=jar",
                "-Dmdep.outputFile=.cp.tmp",
                f"-Darrow.version={self.env.config.version}",
            ],
            cwd=path_arrow_project,
            text=True,
        )
        if not (path_arrow_project / ".cp.tmp").exists():
            raise RuntimeError(
                __("invalid process to create jshell dependencies library")
            )

        # get list of all arrow jar dependencies
        with open(path_arrow_project / ".cp.tmp") as f:
            stdout_dependency = f.read()
        if not stdout_dependency:
            raise RuntimeError(
                __("invalid process to list jshell dependencies library")
            )

        # execute java testing code thru jshell and read output
        # JDK11 support '-' This allows the pipe to work as expected without requiring a shell
        # Migrating to /dev/stdin to also support JDK9+
        proc_jshell_process = subprocess.Popen(
            ["jshell", "-R--add-opens=java.base/java.nio=ALL-UNNAMED", "--class-path", stdout_dependency, "-s", "/dev/stdin"],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            text=True,
        )
        out_java_arrow, err_java_arrow = proc_jshell_process.communicate(code)
        if err_java_arrow:
            raise RuntimeError(__("invalid process to run jshell"))

        # continue with python logic code to do java output validation
        output = f"print('''{self.clean_output(out_java_arrow)}''')"

        # continue with sphinx default logic
        return compile(output, name, self.type, flags, dont_inherit)

    def clean_output(self, output: str):
        if output[-3:] == '-> ':
            output = output[:-3]
        if output[-1:] == '\n':
            output = output[:-1]
        output = (4*' ').join(output.split('\t'))
        return output

def setup(app) -> Dict[str, Any]:
    app.add_directive("testcode", JavaTestcodeDirective)
    app.add_directive("testoutput", TestoutputDirective)
    app.add_builder(JavaDocTestBuilder)
    # this config value adds to sys.path
    app.add_config_value("doctest_path", [], False)
    app.add_config_value("doctest_test_doctest_blocks", "default", False)
    app.add_config_value("doctest_global_setup", "", False)
    app.add_config_value("doctest_global_cleanup", "", False)
    app.add_config_value(
        "doctest_default_flags",
        doctest.DONT_ACCEPT_TRUE_FOR_1
        | doctest.ELLIPSIS
        | doctest.IGNORE_EXCEPTION_DETAIL,
        False,
    )
    return {"version": sphinx.__display_version__, "parallel_read_safe": True}
