"""Obsidian domain value objects."""

from __future__ import annotations

import re
from dataclasses import dataclass

from .exceptions import (
    InvalidCanvasError,
    InvalidNoteError,
    InvalidReferenceError,
    InvalidTagError,
    InvalidTemplateError,
    InvalidTaskError,
    InvalidVaultError,
)


def _normalize_text(value: str, *, error_cls: type[Exception], field_name: str) -> str:
    if not isinstance(value, str):
        raise error_cls(f"{field_name} must be a string")
    normalized = value.strip()
    if not normalized:
        raise error_cls(f"{field_name} cannot be empty")
    return normalized


@dataclass(frozen=True)
class VaultName:
    value: str

    def __post_init__(self) -> None:
        object.__setattr__(
            self, "value", _normalize_text(self.value, error_cls=InvalidVaultError, field_name="vault name")
        )


@dataclass(frozen=True)
class NoteTitle:
    value: str

    def __post_init__(self) -> None:
        object.__setattr__(
            self, "value", _normalize_text(self.value, error_cls=InvalidNoteError, field_name="note title")
        )


@dataclass(frozen=True)
class NotePath:
    value: str

    def __post_init__(self) -> None:
        if not isinstance(self.value, str):
            raise InvalidReferenceError("note path must be a string")
        normalized = self.value.strip().replace("\\", "/")
        normalized = re.sub(r"/+", "/", normalized)
        normalized = normalized.lstrip("/")
        if not normalized:
            raise InvalidReferenceError("note path cannot be empty")
        if normalized in {".", ".."} or "/../" in f"/{normalized}/" or "/./" in f"/{normalized}/":
            raise InvalidReferenceError("note path cannot contain relative traversal segments")
        object.__setattr__(self, "value", normalized)


@dataclass(frozen=True)
class TagName:
    value: str

    def __post_init__(self) -> None:
        normalized = _normalize_text(self.value, error_cls=InvalidTagError, field_name="tag")
        normalized = normalized.lstrip("#").strip().lower()
        if not normalized:
            raise InvalidTagError("tag cannot be empty")
        object.__setattr__(self, "value", normalized)


@dataclass(frozen=True)
class Alias:
    value: str

    def __post_init__(self) -> None:
        object.__setattr__(
            self, "value", _normalize_text(self.value, error_cls=InvalidReferenceError, field_name="alias")
        )


@dataclass(frozen=True)
class TemplateName:
    value: str

    def __post_init__(self) -> None:
        object.__setattr__(
            self, "value", _normalize_text(self.value, error_cls=InvalidTemplateError, field_name="template name")
        )


@dataclass(frozen=True)
class CanvasName:
    value: str

    def __post_init__(self) -> None:
        object.__setattr__(
            self, "value", _normalize_text(self.value, error_cls=InvalidCanvasError, field_name="canvas name")
        )


@dataclass(frozen=True)
class TaskText:
    value: str

    def __post_init__(self) -> None:
        object.__setattr__(
            self, "value", _normalize_text(self.value, error_cls=InvalidTaskError, field_name="task text")
        )
