# gophian -- tools to help with Debianizing Go software
# Copyright (C) 2024-2025 Maytham Alsudany <maytha8thedev@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

# package = repo root with go.mod
# module = Go root package and packages located in subdir of module
# import_path = import path of Go module
# deb_package = Debian package

import subprocess
from typing import Dict, List, Self

import requests

from gophian.error import GoExecutionError
from gophian.session.git import GoPackage
from gophian.tempdir import TemporaryDirectory
from gophian.vcs import package_from_import_path


class Session:
    """
    A session with cloned repos in a GOPATH-like temporary directory.
    """

    def __init__(self, requests_session: requests.Session) -> None:
        self.requests_session = requests_session
        self.tempdir = TemporaryDirectory()
        self.gopath = self.tempdir.path
        self.stdlib = stdlib()

    def cleanup(self) -> None:
        self.tempdir.cleanup()

    def __enter__(self) -> Self:
        return self

    def __exit__(self, *_) -> None:
        self.cleanup()

    downloaded_packages: Dict[str, GoPackage] = {}

    def go_get(self, import_path: str) -> GoPackage:
        package_import_path, repo = package_from_import_path(
            self.requests_session, import_path
        )

        if package_import_path in self.downloaded_packages:
            return self.downloaded_packages[package_import_path]

        package = GoPackage(self, package_import_path, repo)
        self.downloaded_packages[package_import_path] = package
        return package

    def package_modules(self, package: str) -> List[str]:
        repo = self.go_get(package)

        out = subprocess.run(
            ["env", f"GOPATH={self.gopath}", "go", "list", package + "/..."],
            capture_output=True,
            cwd=repo.path,
        )

        return out.stdout.decode().splitlines()

    def package_main_modules(self, package: str) -> List[str]:
        modules = self.package_modules(package)
        main_modules: List[str] = []
        for module in modules:
            out = subprocess.run(
                [
                    "env",
                    f"GOPATH={self.gopath}",
                    "go",
                    "list",
                    "-f",
                    "{{.Name}}",
                    module,
                ],
                capture_output=True,
                cwd=self.gopath / "src" / package,
            )
            if out.stdout.decode().strip() == "main":
                main_modules.append(module)
        return main_modules


def stdlib() -> List[str]:
    out = subprocess.run(
        ["go", "list", "std"],
        capture_output=True,
    )
    if out.returncode != 0:
        raise GoExecutionError(out)
    return out.stdout.decode().splitlines() + ["C"]
