"""Core functionality for writing version information to files."""

from __future__ import annotations

import logging
import warnings
from pathlib import Path
from typing import TYPE_CHECKING

from ._version_cls import _version_as_tuple

if TYPE_CHECKING:
    from . import _types as _t
    from ._scm_version import ScmVersion

log = logging.getLogger(__name__)


class MISSING:
    def __repr__(self) -> str:
        return "<MISSING>"

    def __bool__(self) -> bool:
        return False


MISSING_VAL = MISSING()


DEFAULT_TEMPLATES = {
    ".py": """\
# file generated by vcs-versioning
# don't change, don't track in version control
from __future__ import annotations

__all__ = [
    "__version__",
    "__version_tuple__",
    "version",
    "version_tuple",
    "__commit_id__",
    "commit_id",
]

version: str
__version__: str
__version_tuple__: tuple[int | str, ...]
version_tuple: tuple[int | str, ...]
commit_id: str | None
__commit_id__: str | None

__version__ = version = {version!r}
__version_tuple__ = version_tuple = {version_tuple!r}

__commit_id__ = commit_id = {scm_version.short_node!r}
""",
    ".txt": "{version}",
}


class DummyScmVersion:
    """Placeholder for when no ScmVersion is available."""

    @property
    def short_node(self) -> str | None:
        return None


def _validate_template(target: Path, template: str | None) -> str:
    """Validate and return the template to use for writing the version file.

    Args:
        target: The target file path
        template: User-provided template or None to use default

    Returns:
        The template string to use

    Raises:
        ValueError: If no suitable template is found
    """
    if template == "":
        warnings.warn(
            f"{template=} looks like a error, using default instead", stacklevel=2
        )
        template = None
    if template is None:
        template = DEFAULT_TEMPLATES.get(target.suffix)

    if template is None:
        raise ValueError(
            f"bad file format: {target.suffix!r} (of {target})\n"
            "only *.txt and *.py have a default template"
        )
    else:
        return template


def write_version_to_path(
    target: Path,
    template: str | None,
    version: str,
    scm_version: ScmVersion | MISSING | None = MISSING_VAL,
) -> None:
    """Write version information to a file using a template.

    Args:
        target: The target file path to write to
        template: Template string or None to use default based on file extension
        version: The version string to write
        scm_version: Optional ScmVersion object for additional metadata
    """
    final_template = _validate_template(target, template)
    log.debug("dump %s into %s", version, target)
    version_tuple = _version_as_tuple(version)
    if scm_version is MISSING_VAL:
        warnings.warn(
            "write_version_to_path called without scm_version parameter. "
            "This will be required in a future version. "
            "Pass scm_version=None explicitly to suppress this warning.",
            DeprecationWarning,
            stacklevel=2,
        )

    content = final_template.format(
        version=version,
        version_tuple=version_tuple,
        scm_version=scm_version or DummyScmVersion(),
    )

    target.write_text(content, encoding="utf-8")


def dump_version(
    root: _t.PathT,
    version: str,
    write_to: _t.PathT,
    template: str | None = None,
    scm_version: ScmVersion | MISSING | None = MISSING_VAL,
) -> None:
    """Write version information to a file relative to root.

    Args:
        root: The root directory (project root)
        version: The version string to write
        write_to: The target file path (relative to root or absolute)
        template: Template string or None to use default
        scm_version: Optional ScmVersion object for additional metadata
    """
    assert isinstance(version, str)
    root = Path(root)
    write_to = Path(write_to)
    if write_to.is_absolute():
        # trigger warning on escape
        write_to.relative_to(root)
        warnings.warn(
            f"{write_to=!s} is a absolute path,"
            " please switch to using a relative version file",
            DeprecationWarning,
            stacklevel=2,
        )
        target = write_to
    else:
        target = Path(root).joinpath(write_to)
    write_version_to_path(
        target, template=template, version=version, scm_version=scm_version
    )
