import re
from functools import cached_property
from pathlib import Path
from textwrap import dedent
from typing import Any

from hatchling.version.core import DEFAULT_PATTERN
from hatchling.version.source.plugin.interface import VersionSourceInterface
from pydantic import BaseModel, Field

from ..common.gradle import GradleVersion, load_properties

PY_VERSION_REGEX = re.compile(
    r'(?i)^(PY_VERSION) *= *([\'"])v?(?P<version>.+?)\2',
    flags=re.MULTILINE,
)

DEFAULT_REGEX = re.compile(DEFAULT_PATTERN, flags=re.MULTILINE)


class PropertiesVersionSourceConfig(BaseModel):
    py_path: Path = Field(alias="py-path")

    gradle_path: Path = Field(alias="gradle-path", default=Path("gradle.properties"))
    key: str = "modVersion"


class PropertiesVersionSource(VersionSourceInterface):
    PLUGIN_NAME = "gradle-properties"

    def get_version_data(self) -> dict[str, Any]:
        # load gradle version from gradle.properties
        p = load_properties(self.gradle_path)
        gradle = GradleVersion.from_properties(p, self.key)

        # load py_version from a file
        contents = self.py_path.read_text("utf-8")
        match = PY_VERSION_REGEX.search(contents) or DEFAULT_REGEX.search(contents)
        if not match:
            raise ValueError(
                f"Unable to parse the version from the file: {self.py_path}"
            )
        py_version = match["version"]

        return {
            "version": gradle.full_version(py_version),
            "gradle_version": gradle,
            "py_version": py_version,
        }

    def set_version(self, version: str, version_data: dict[str, Any]) -> None:
        self.py_path.write_text(
            dedent(
                f"""\
                # This file is auto-generated by hatch-gradle-version.
                # Only the value of PY_VERSION is editable.
                # All changes to other values will be ignored and overwritten.

                PY_VERSION = "{version_data["py_version"]}"

                # Everything below this line is completely auto-generated.

                GRADLE_VERSION = "{version_data["gradle_version"]}"
                FULL_VERSION = "{version}"
                """
            )
        )

    # config values

    @cached_property
    def typed_config(self):
        return PropertiesVersionSourceConfig.model_validate(self.config)

    @property
    def gradle_path(self):
        path = Path(self.root) / self.typed_config.gradle_path
        if not path.is_file():
            raise FileNotFoundError(
                f"File does not exist or is not a file: {self.typed_config.gradle_path}"
            )
        return path

    @property
    def py_path(self):
        path = Path(self.root) / self.typed_config.py_path
        if not path.is_file():
            raise FileNotFoundError(
                f"File does not exist or is not a file: {self.typed_config.py_path}"
            )
        return path

    @property
    def key(self):
        return self.typed_config.key
