from __future__ import annotations

import os
import re

DEFAULT_PATTERN = r'(?i)^(__version__|VERSION) *= *([\'"])v?(?P<version>.+?)\2'
DEFAULT_TEMPLATE = """\
# This file is auto-generated by Hatchling. As such, do not:
#   - modify
#   - track in version control e.g. be sure to add to .gitignore
__version__ = VERSION = {version!r}
"""


class VersionFile:
    def __init__(self, root: str, relative_path: str) -> None:
        self.__relative_path = relative_path
        self.__path = os.path.normpath(os.path.join(root, relative_path))
        self.__cached_read_data: tuple | None = None

    def read(self, *, pattern: str | bool) -> str:
        if not os.path.isfile(self.__path):
            message = f'file does not exist: {self.__relative_path}'
            raise OSError(message)

        with open(self.__path, encoding='utf-8') as f:
            contents = f.read()

        if not pattern or pattern is True:
            pattern = DEFAULT_PATTERN

        match = re.search(pattern, contents, flags=re.MULTILINE)
        if not match:
            message = f'unable to parse the version from the file: {self.__relative_path}'
            raise ValueError(message)

        groups = match.groupdict()
        if 'version' not in groups:
            message = 'no group named `version` was defined in the pattern'
            raise ValueError(message)

        self.__cached_read_data = groups['version'], contents, match.span('version')
        return self.__cached_read_data[0]

    def set_version(self, version: str) -> None:
        _old_version, file_contents, (start, end) = self.__cached_read_data  # type: ignore[misc]
        with open(self.__path, 'w', encoding='utf-8') as f:
            f.write(f'{file_contents[:start]}{version}{file_contents[end:]}')

    def write(self, version: str, template: str = DEFAULT_TEMPLATE) -> None:
        template = template or DEFAULT_TEMPLATE

        parent_dir = os.path.dirname(self.__path)
        if not os.path.isdir(parent_dir):
            os.makedirs(parent_dir)

        with open(self.__path, 'w', encoding='utf-8') as f:
            f.write(template.format(version=version))
