r"""PKGBUILD
============
"""
import os
from typing import Any

from markdown_it.token import Token
from tree_sitter_lsp.misc import get_md_tokens, get_soup

from .._metainfo import SOURCE, project
from .licenses import LICENSES


def get_content(tokens: list[Token]) -> str:
    r"""Get content.

    :param tokens:
    :type tokens: list[Token]
    :rtype: str
    """
    return "\n".join(
        [token.content.replace("\n", " ") for token in tokens if token.content]
    )


def init_schema() -> dict[str, Any]:
    r"""Init schema.

    :rtype: dict[str, dict[str, Any]]
    """
    schemas = {}
    for filetype in {"PKGBUILD", "install"}:
        schemas[filetype] = {
            "$id": f"{SOURCE}/blob/main/src/termux_language_server/assets/json/{filetype}.json",
            "$schema": "http://json-schema.org/draft-07/schema#",
            "$comment": (
                "Don't edit this file directly! It is generated by "
                f"`{project} --generate-schema={filetype}`."
            ),
            "type": "object",
            "patternProperties": {},
        }
    # PKGBUILD has variables
    schemas["PKGBUILD"]["properties"] = {}

    tokens = get_md_tokens("PKGBUILD")
    # **pkgname (array)**
    #
    # > Either the name of the package or an array of names for split
    # > packages. Valid characters for members of this array are
    # > alphanumerics, and any of the following characters: "@ . \_ + -".
    # > Additionally, names are not allowed to start with hyphens or dots.
    indices = [
        index
        for index, token in enumerate(tokens)
        if token.content.startswith("**")
        and token.content.endswith("**")
        and token.level == 1
    ]
    blockquote_close_indices = [
        index
        for index, token in enumerate(tokens)
        if token.type == "blockquote_close"
    ]
    close_indices = [
        min(
            [
                blockquote_close_index
                for blockquote_close_index in blockquote_close_indices
                if blockquote_close_index > index
            ]
        )
        for index in indices
    ]
    for index, close_index in zip(indices, close_indices):
        children = tokens[index].children
        if children is None:
            continue
        words = [child.rstrip(",") for child in children[2].content.split()]
        # md5sums, ...
        if len(words) > 2:
            _words = [child for child in children[4].content.split()]
            kind = (
                _words[1].lstrip("(").rstrip(")")
                if len(words) > 1
                else "string"
            )
            names = words + [_words[0]]
        else:
            names = [words[0].rstrip("()")]
            kind = (
                words[1].lstrip("(").rstrip(")")
                if len(words) > 1
                else "string"
            )
        for name in names:
            description = get_content(tokens[index + 1 : close_index])
            if name.startswith("pre_") or name.startswith("post_"):
                filetype = "install"
                kind = "Function"
            else:
                filetype = "PKGBUILD"
            if kind == "Function":
                name = rf"^{name}($|_.*)"
                properties_name = "patternProperties"
            else:
                properties_name = "properties"
            schemas[filetype][properties_name][name] = {
                "description": description
            }
            # makepkg supports building multiple packages from a single PKGBUILD.
            # This is achieved by assigning an array of package names to the
            # pkgname directive.
            if name == "pkgname":
                schemas[filetype][properties_name][name]["oneOf"] = [
                    {
                        "type": "array",
                        "items": {"type": "string"},
                        "uniqueItems": True,
                    },
                    {"type": "string"},
                ]
            elif kind == "string":
                schemas[filetype][properties_name][name]["type"] = "string"
            elif kind in {"array", "arrays"}:
                schemas[filetype][properties_name][name] |= {
                    "type": "array",
                    "items": {"type": "string"},
                    "uniqueItems": True,
                }
            elif kind == "Function":
                # Each split package uses a corresponding packaging function with
                # name package_foo(), where foo is the name of the split package.
                schemas[filetype][properties_name][name]["const"] = 0
    # https://archlinux32.org/architecture/
    # https://archlinux.org/packages/
    # https://archlinuxarm.org/forum/viewforum.php?f=56
    schemas["PKGBUILD"]["properties"]["arch"]["items"]["enum"] = [
        "any",
        "pentium4",
        "i486",
        "i686",
        "x86_64",
        "x86_64_v3",
        "arm",
        "armv6h",
        "armv7h",
        "armv8",
        "aarch64",
    ]
    schemas["PKGBUILD"]["properties"]["url"]["format"] = "uri"
    del schemas["PKGBUILD"]["properties"]["pkgver"]["type"]
    schemas["PKGBUILD"]["properties"]["pkgver"] |= {
        "oneOf": [{"type": "string"}, {"const": 0}]
    }

    soup = get_soup("https://www.msys2.org/dev/pkgbuild/")
    for tr in soup.find_all("tr")[1:]:
        tds = tr.find_all("td")
        name = tds[0].code.text
        kind = tds[1].text
        kind = {"mapping": "array"}.get(kind, kind)
        description = tds[2].text
        schemas["PKGBUILD"]["properties"][name] = {
            "type": kind,
            "description": description,
        }
        if kind == "array":
            schemas["PKGBUILD"]["properties"][name] |= {
                "items": {"type": "string"},
                "uniqueItems": True,
            }
        elif kind == "object":
            schemas["PKGBUILD"]["properties"][name] |= {"properties": {}}
        elif kind == "string" and name.endswith("_url"):
            schemas["PKGBUILD"]["properties"][name]["format"] = "uri"
    # https://packages.msys2.org/repos
    schemas["PKGBUILD"]["properties"]["mingw_arch"]["items"]["enum"] = [
        "mingw32",
        "mingw64",
        "ucrt64",
        "clang64",
        "clang32",
        "clangarm64",
    ]
    names = []
    for li in soup.find_all("li"):
        code = li.find("code")
        if code is None:
            continue
        name = code.text
        if not li.text.startswith(name):
            continue
        names += [name]
        # text = li.text
        # _, _, text = text.partition(name)
        # description = text.replace("\n", " ").lstrip("- ")
    schemas["PKGBUILD"]["properties"]["msys2_references"]["items"][
        "pattern"
    ] = f"({'|'.join(names)})(|: .*)"
    schemas["PKGBUILD"]["properties"]["license"]["items"] = {
        "oneOf": [
            {
                "type": "string",
                "enum": LICENSES
                + os.listdir("/usr/share/licenses/common")
                + ["custom"],
            },
            {"type": "string", "pattern": "custom:.+"},
        ]
    }
    return schemas
