#     Copyright 2025, QutaYba, nasr2python@gmail.com find license text at end of file


"""
Namespace packages of Python3

"""

import os

from qutayba.nodes.AttributeNodes import makeExpressionAttributeLookup
from qutayba.nodes.CallNodes import ExpressionCallNoKeywords
from qutayba.nodes.ConstantRefNodes import makeConstantRefNode
from qutayba.nodes.ContainerMakingNodes import (
    makeExpressionMakeList,
    makeExpressionMakeTuple,
    makeExpressionMakeTupleOrConstant,
)
from qutayba.nodes.FutureSpecs import FutureSpec
from qutayba.nodes.ImportNodes import (
    ExpressionImportName,
    makeExpressionImportModuleFixed,
    makeExpressionImportModuleNameHard,
)
from qutayba.nodes.ModuleAttributeNodes import ExpressionModuleAttributeFileRef
from qutayba.nodes.ModuleNodes import CompiledPythonNamespacePackage
from qutayba.nodes.VariableNameNodes import StatementAssignmentVariableName
from qutayba.Options import getFileReferenceMode
from qutayba.PythonVersions import python_version

from .FutureSpecState import popFutureSpec, pushFutureSpec
from .TreeHelpers import (
    buildNode,
    makeStatementsSequenceFromStatement,
    parseSourceCodeToAst,
)
from .VariableClosure import completeVariableClosures


def _makeCall(module_name, import_name, attribute_name, source_ref, *args):
    return ExpressionCallNoKeywords(
        called=makeExpressionAttributeLookup(
            expression=makeExpressionImportModuleNameHard(
                module_name=module_name,
                import_name=import_name,
                module_guaranteed=True,
                source_ref=source_ref,
            ),
            attribute_name=attribute_name,
            source_ref=source_ref,
        ),
        args=makeExpressionMakeTupleOrConstant(
            elements=args, user_provided=True, source_ref=source_ref
        ),
        source_ref=source_ref,
    )


def _getNameSpacePathExpression(package, source_ref):
    """Create the __path__ expression for a package."""

    reference_mode = getFileReferenceMode()

    if reference_mode == "original":
        return makeConstantRefNode(
            constant=[package.getCompileTimeDirectory()],
            source_ref=source_ref,
        )
    elif reference_mode == "frozen":
        return makeConstantRefNode(
            constant=[],
            source_ref=source_ref,
        )
    else:
        file_ref_node = ExpressionModuleAttributeFileRef(
            variable=package.getVariableForReference("__file__"),
            source_ref=source_ref,
        )

        if package.isCompiledPythonNamespacePackage():
            elements = [file_ref_node]
        else:
            elements = [
                ExpressionCallNoKeywords(
                    called=makeExpressionAttributeLookup(
                        expression=makeExpressionImportModuleNameHard(
                            module_name="os",
                            import_name="path",
                            module_guaranteed=True,
                            source_ref=source_ref,
                        ),
                        attribute_name="dirname",
                        source_ref=source_ref,
                    ),
                    args=makeExpressionMakeTuple(
                        elements=(file_ref_node,),
                        source_ref=source_ref,
                    ),
                    source_ref=source_ref,
                )
            ]

        if package.canHaveExternalImports():
            parts = package.getFullName().asString().split(".")

            for count in range(len(parts)):
                path_part = _makeCall(
                    "os",
                    "environ",
                    "get",
                    source_ref,
                    makeConstantRefNode(
                        constant="DEVILPY_PACKAGE_%s" % "_".join(parts[: count + 1]),
                        source_ref=source_ref,
                    ),
                    makeConstantRefNode(
                        constant=os.path.sep + "not_existing", source_ref=source_ref
                    ),
                )

                if parts[count + 1 :]:
                    path_part = _makeCall(
                        "os",
                        "path",
                        "join",
                        source_ref,
                        path_part,
                        makeConstantRefNode(
                            constant=os.path.join(*parts[count + 1 :]),
                            source_ref=source_ref,
                        ),
                    )

                elements.append(path_part)

        return makeExpressionMakeList(elements=tuple(elements), source_ref=source_ref)


def createPathAssignment(package, source_ref):
    return StatementAssignmentVariableName(
        provider=package,
        variable_name="__path__",
        source=_getNameSpacePathExpression(package=package, source_ref=source_ref),
        source_ref=source_ref,
    )


def _getPathFinderFunction(package, source_ref):
    # TODO: This could be a shared helper function, but we currently cannot do these.
    ast_tree = parseSourceCodeToAst(
        source_code="_path_finder = lambda *args, **kw: None",
        module_name=package.getFullName(),
        filename=source_ref.getFilename(),
        line_offset=0,
    )

    pushFutureSpec(package.getFullName())
    statement = buildNode(package, ast_tree.body[0], source_ref)
    popFutureSpec()

    return statement.subnode_source


def createPython3NamespacePath(package, source_ref):

    module_name = (
        "_frozen_importlib" if python_version < 0x350 else "_frozen_importlib_external"
    )

    return StatementAssignmentVariableName(
        provider=package,
        variable_name="__path__",
        source=ExpressionCallNoKeywords(
            called=ExpressionImportName(
                module=makeExpressionImportModuleFixed(
                    using_module_name=package.getFullName(),
                    module_name=module_name,
                    value_name=module_name,
                    source_ref=source_ref,
                ),
                import_name="_NamespacePath",
                level=0,
                source_ref=source_ref,
            ),
            args=makeExpressionMakeTupleOrConstant(
                elements=(
                    makeConstantRefNode(
                        constant=package.getFullName().asString(),
                        user_provided=True,
                        source_ref=source_ref,
                    ),
                    _getNameSpacePathExpression(package=package, source_ref=source_ref),
                    _getPathFinderFunction(package=package, source_ref=source_ref),
                ),
                user_provided=True,
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        ),
        source_ref=source_ref,
    )


def createNamespacePackage(module_name, reason, is_top, source_ref):
    package = CompiledPythonNamespacePackage(
        module_name=module_name,
        reason=reason,
        is_top=is_top,
        mode="compiled",
        future_spec=FutureSpec(use_annotations=False),
        source_ref=source_ref,
    )

    if python_version >= 0x300:
        statement = createPython3NamespacePath(package=package, source_ref=source_ref)
    else:
        statement = createPathAssignment(package, source_ref)

    package.setChildBody(makeStatementsSequenceFromStatement(statement=statement))

    completeVariableClosures(package)

    return package



