# This file is part of Gajim.
#
# SPDX-License-Identifier: GPL-3.0-only

from __future__ import annotations

from typing import Any

import json
import sys
from dataclasses import dataclass
from pathlib import Path

from packaging.requirements import Requirement
from packaging.version import Version as V

import gajim

from .helpers import is_shipped_plugin
from .plugins_i18n import _ as p_

BLOCKED_PLUGINS = [
    "appindicator_integration",
    "omemo",
    "openpgp",
    "plugin_installer",
    "syntax_highlight",
    "url_image_preview",
]

GAJIM_VERSION = V(gajim.__version__.split("+", 1)[0]).base_version


@dataclass(frozen=True, eq=True)
class PluginManifest:
    name: str
    short_name: str
    description: str
    authors: list[str]
    homepage: str
    config_dialog: bool
    version: V
    requirements: list[Requirement]
    platforms: list[str]
    path: Path | None = None

    def __hash__(self):
        return hash(f"{self.short_name}_{self.version}")

    @property
    def is_usable(self) -> bool:
        if self.short_name in BLOCKED_PLUGINS:
            return False

        if not self._check_requirements():
            return False

        platform = sys.platform
        if platform not in ("win32", "darwin", "linux"):
            # sys.platform can return an unknown amount of unix/linux derivates
            platform = "others"

        return platform in self.platforms

    @property
    def is_shipped(self) -> bool:
        if self.path is None:
            return False
        return is_shipped_plugin(self.path)

    def get_remote_url(self, repository_url: str) -> str:
        filename = f"{self.short_name}_{self.version}.zip"
        return f"{repository_url}/{self.short_name}/{filename}"

    @classmethod
    def from_path(cls, path: Path) -> PluginManifest:
        manifest_path = path / "plugin-manifest.json"
        if not manifest_path.exists():
            raise ValueError(f"Not a plugin path: {path}")

        if manifest_path.is_dir():
            raise ValueError(f"Not a plugin path: {path}")

        with manifest_path.open(encoding="utf8") as f:
            try:
                manifest = json.load(f)
            except Exception as error:
                raise ValueError(f"Error while parsing manifest: {path}, {error}")
        return cls.from_manifest_json(manifest, manifest_path)

    def _check_requirements(self) -> bool:
        return any(GAJIM_VERSION in req.specifier for req in self.requirements)

    @classmethod
    def from_manifest_json(
        cls, manifest: dict[str, Any], path: Path | None = None
    ) -> PluginManifest:
        return cls(
            name=manifest["name"],
            short_name=manifest["short_name"],
            description=p_(manifest["description"]),
            authors=manifest["authors"],
            homepage=manifest["homepage"],
            config_dialog=manifest["config_dialog"],
            version=V(manifest["version"]),
            requirements=[Requirement(r) for r in manifest["requirements"]],
            platforms=manifest["platforms"],
            path=path.parent if path is not None else path,
        )
