import os
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 model_validator

from ..common.gradle import GradleVersion, load_properties
from ..common.model import KebabModel
from ..common.path import assert_exists

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 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"]

        version = gradle.full_version(py_version)

        # write here because otherwise the other version constants get outdated
        version_data = {
            "version": version,
            "gradle_version": gradle,
            "py_version": py_version,
        }
        self.set_version(version, version_data)
        return version_data

    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. Do not edit other values.

                PY_VERSION = "{version_data["py_version"]}"
                """
            )
        )
        self.py_path.with_name("__gradle_version__.py").write_text(
            dedent(
                f"""\
                # This file is auto-generated by hatch-gradle-version. Do not edit.

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

    # config values

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

    @property
    def gradle_path(self):
        path = Path(self.root) / self.typed_config.gradle_path
        assert_exists(path)
        return path

    @property
    def py_path(self):
        path = Path(self.root) / self.typed_config.py_path
        assert_exists(path)
        return path

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

    class Config(KebabModel):
        source: str
        py_path: Path

        scheme: str | None = None
        gradle_path: Path = Path("gradle.properties")
        key: str = "modVersion"

        @model_validator(mode="after")
        def _prepend_gradle_dir(self):
            gradle_dir = os.getenv("HATCH_GRADLE_DIR")
            if gradle_dir is None:
                return self

            self.gradle_path = Path(gradle_dir) / self.gradle_path
            return self
