# 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/>.

import re
from typing import Tuple
from urllib.parse import quote as urlencode, urlparse
from bs4 import BeautifulSoup, Tag

import requests

from gophian.error import GophianError

GO_IMPORT_META_CONTENT_REGEX = re.compile(r"(\S*)\s+git\s+(\S*)")


def package_from_import_path(session: requests.Session, path: str) -> Tuple[str, str]:
    """
    Get the root package and repo from the given import path.
    """
    if path.split("/")[0] == "github.com":
        repo = "/".join(path.split("/")[0:3])
        return (repo, strip_git_suffix("https://" + repo))
    try:
        return get_repo_meta(session, path)
    except NoRootRepoError:
        raise NoRootRepoError(path)


def get_repo_meta(session: requests.Session, path: str) -> Tuple[str, str]:
    """
    Get the root package and repo by recursively downloading and checking
    website pages for a 'go-import' meta tag.
    """
    res = session.get(
        "https://" + path,
        timeout=60,
        params={"go-get": "1"},
        headers={"Accept": "text/html,application/xhtml+xml,application/xml"},
    )
    soup = BeautifulSoup(res.text, "html.parser")

    def is_go_import_meta(tag: Tag):
        return (
            tag.name == "meta"
            and tag.get("name") == "go-import"
            and tag.has_attr("content")
        )

    match = soup.find(is_go_import_meta)
    parts = None
    if match and isinstance(match, Tag):
        content = match.get_attribute_list("content")[0]
        if content:
            parts = GO_IMPORT_META_CONTENT_REGEX.match(content)
    if parts is None:
        parts = path.split("/")
        if len(parts[:-1]) < 1:
            raise NoRootRepoError(path)
        else:
            return get_repo_meta(session, "/".join(parts[:-1]))
    return (
        parts.group(1),
        strip_git_suffix(parts.group(2)),
    )


def get_short_desc(session: requests.Session, repo_url: str) -> str:
    url = urlparse(repo_url)
    path = url.path[1:]
    desc = None

    if url.hostname == "github.com":
        repo = session.get(f"https://api.github.com/repos/{path}", timeout=60).json()
        if repo["description"] is not None and len(repo["description"]) > 0:
            desc = repo["description"]

    if url.hostname == ["codeberg.org", "gitea.com"]:
        repo = session.get(
            f"https://{url.hostname}/api/v1/repos/{path}", timeout=60
        ).json()
        if repo["description"] is not None and len(repo["description"]) > 0:
            desc = repo["description"]

    if url.hostname in ["salsa.debian.org", "gitlab.com"]:
        repo = session.get(
            f"https://{url.hostname}/api/v4/projects/{urlencode(path)}", timeout=60
        ).json()
        if repo["description"] is not None and len(repo["description"]) > 0:
            desc = repo["description"]

    if desc is None:
        return "TODO"

    desc = remove_punctuation(desc)
    desc = remove_article(desc)
    return desc


def remove_punctuation(s: str) -> str:
    if s[-1] in [".", "!", "?"]:
        return s[:-1]
    else:
        return s


def remove_article(s: str) -> str:
    if s.lower().startswith("a "):
        return s[2:]
    elif s.lower().startswith("an "):
        return s[3:]
    else:
        return s


def strip_git_suffix(s: str) -> str:
    if s.endswith(".git/"):
        return s[:-5]
    if s.endswith(".git"):
        return s[:-4]
    return s


class NoRootRepoError(GophianError):
    def __init__(self, path: str):
        super().__init__(f"No root repo could be determined for the path '{path}'")
