from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING

from oslandia.__about__ import DIR_PLUGIN_ROOT
from oslandia.toolbelt.json_tools import json_to_dict


if TYPE_CHECKING:
    from pathlib import Path


@dataclass
class ProjectObj:
    """Object describing a project."""

    name: str
    project_type: str
    version: str
    plugin_id: str | None = None
    about: str | None = None
    file_name: str | None = None
    experimental: str | None = None
    description_en: str | None = None
    description_fr: str | None = None
    logo: str | None = None
    tags: str | None = None
    funders: str | None = None
    doc_url: str | None = None
    code_url: str | None = None
    screenshot: list[str] = field(default_factory=list)
    issue_to_be_funded: str | None = None
    blog_link: str | None = None

    @classmethod
    def from_dict(cls, data: dict, project_type: str) -> ProjectObj:
        """Create ProjectObj from a dictionary.

        :param data: Dictionary from which to create the instance
        :type data: Dict
        :param project_dict: Type of projet (plugins or other)
        :param project_dict: str
        """
        return cls(
            name=data["name"],
            project_type=project_type,
            version=data["version"],
            plugin_id=data.get("plugin_id"),
            about=data.get("about"),
            file_name=data.get("file_name"),
            experimental=data.get("experimental"),
            description_en=data["description"].get("en"),
            description_fr=data["description"].get("fr"),
            logo=data.get("logo"),
            tags=data.get("tags"),
            funders=data.get("funders"),
            doc_url=data.get("doc_url"),
            code_url=data.get("code_url"),
            screenshot=data.get("screenshot") or [],
            issue_to_be_funded=data.get("issue_to_be_funded"),
            blog_link=data.get("blog_link"),
        )


@dataclass
class ProjectsObj:
    public_plugins: list[ProjectObj] = field(default_factory=list)
    gitlab_plugins: list[ProjectObj] = field(default_factory=list)
    private_plugins: list[ProjectObj] = field(default_factory=list)
    other_projets: list[ProjectObj] = field(default_factory=list)

    def __init__(self, cache_path: Path) -> None:
        """ProjectsObj object This object contains attributes that are lists of ProjectObj. This class is described by a single attribute, the path to the json files that make up the lists.

        :param cache_path: Path to json to build objects.
        :type cache_path: Path
        """
        self.public_plugins = self._load_plugins(
            cache_path / "showcase/public_plugins.json", "public_plugins", "plugin"
        )
        self.gitlab_plugins = self._load_plugins(
            cache_path / "showcase/gitlab_plugins.json", "gitlab_plugins", "plugin"
        )
        self.private_plugins = self._load_plugins(
            cache_path / "showcase/private_plugins.json", "private_plugins", "plugin"
        )
        self.other_projets = self._load_plugins(
            DIR_PLUGIN_ROOT / "resources/showcase/other_projects.json",
            "other_projects",
            "other",
        )
        self.qgis_projets = self._load_plugins(
            DIR_PLUGIN_ROOT / "resources/showcase/qgis_projects.json",
            "qgis_projets",
            "qgis",
        )

    def _load_plugins(
        self, json_path: Path, key: str, project_type: str
    ) -> list[ProjectObj]:
        """Load plugins from a JSON file.

        :param json_path: Path to json file
        :type json_path: Path
        :param key: Key in the dict
        :type key: str
        :param project_type: Type of project to assign
        :type project_type: str
        :return: List of ProjectObj
        :rtype: List[ProjectObj]
        """
        data_dict = json_to_dict(json_path)
        return [
            ProjectObj.from_dict(plugin, project_type)
            for plugin in data_dict.get(key, [])
        ]

    @property
    def get_all_projets(self) -> list[ProjectObj]:
        """Return all project

        :return: List of all project class
        :rtype: List[ProjectObj]
        """
        return (
            self.public_plugins
            + self.gitlab_plugins
            + self.private_plugins
            + self.other_projets
            + self.qgis_projets
        )

    def get_projects(self, project_type: str | None = None) -> list[ProjectObj]:
        """Return a sorted combined list of all projects, including public, GitLab,
        private plugins, and other GIS projects. Optionally, filter by project type.

        :param project_type: Optional string to filter projects by type (e.g., "plugin", "other").
        :type project_type: Optional[str]
        :return: Sorted list of ProjectObj instances, filtered by project_type if provided.
        :rtype: List[ProjectObj]
        """

        if project_type:
            result = [
                project
                for project in self.get_all_projets
                if project.project_type == project_type
            ]
            return sorted(result, key=lambda project: project.name.lower())

        return sorted(self.get_all_projets, key=lambda project: project.name.lower())
