"""
Module Settings — Domain Layer

Entity و Value Object‌های سیستم تنظیمات ماژولار.
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Optional
from uuid import UUID, uuid4

from shared.base_classes.entity import TenantEntity
from shared.base_classes.value_object import BaseValueObject


# ─── Value Objects ────────────────────────────────────────────


@dataclass(frozen=True)
class SettingsPath(BaseValueObject):
    """
    مسیر دسترسی به یک تنظیم خاص.

    مثال: "leave.auto_approve_if_balance_sufficient"
    """

    path: str

    def __post_init__(self):
        if not self.path or not self.path.strip():
            raise ValueError("Settings path cannot be empty")
        parts = self.path.split(".")
        for part in parts:
            if not part.isidentifier():
                raise ValueError(
                    f"Invalid path segment: '{part}'. Must be a valid identifier."
                )

    @property
    def segments(self) -> list[str]:
        return self.path.split(".")

    @property
    def root(self) -> str:
        return self.segments[0]

    @property
    def depth(self) -> int:
        return len(self.segments)


@dataclass(frozen=True)
class SettingsFieldDefinition(BaseValueObject):
    """تعریف یک فیلد تنظیم با نوع و اعتبارسنجی."""

    key: str
    field_type: str  # bool, int, float, str, select, multi_select
    label: str
    label_en: str = ""
    description: str = ""
    default_value: Any = None
    required: bool = False
    options: list[dict] | None = None  # for select/multi_select
    min_value: float | None = None
    max_value: float | None = None
    depends_on: str | None = None  # path to another setting


@dataclass(frozen=True)
class SettingsSectionDefinition(BaseValueObject):
    """تعریف یک بخش از تنظیمات."""

    key: str
    label: str
    label_en: str = ""
    description: str = ""
    icon: str | None = None
    order: int = 0
    fields: tuple[SettingsFieldDefinition, ...] = ()


@dataclass(frozen=True)
class SettingsSchemaDefinition(BaseValueObject):
    """تعریف کامل schema تنظیمات یک ماژول."""

    module_key: str
    version: str
    label: str
    label_en: str = ""
    description: str = ""
    sections: tuple[SettingsSectionDefinition, ...] = ()

    def get_default_values(self) -> dict[str, Any]:
        """مقادیر پیش‌فرض را بر اساس schema تولید می‌کند."""
        defaults = {}
        for section in self.sections:
            section_defaults = {}
            for f in section.fields:
                if f.default_value is not None:
                    section_defaults[f.key] = f.default_value
            if section_defaults:
                defaults[section.key] = section_defaults
        return defaults


# ─── Entity ───────────────────────────────────────────────────


@dataclass
class ModuleSettings(TenantEntity):
    """
    تنظیمات یک ماژول برای یک tenant خاص.

    Aggregate Root — مالک تمام تنظیمات ماژول.
    settings_data: ذخیره‌سازی JSONB — schema-driven.
    """

    module_key: str = ""
    settings_data: dict[str, Any] = field(default_factory=dict)

    def get_value(self, path: SettingsPath) -> Any:
        """
        مقدار یک تنظیم را بر اساس مسیر برمی‌گرداند.

        Args:
            path: مسیر تنظیم (مثلاً "leave.max_days_per_request")

        Returns:
            مقدار تنظیم یا None
        """
        current = self.settings_data
        for segment in path.segments:
            if isinstance(current, dict) and segment in current:
                current = current[segment]
            else:
                return None
        return current

    def set_value(self, path: SettingsPath, value: Any) -> None:
        """
        مقدار یک تنظیم را تغییر می‌دهد.

        Args:
            path: مسیر تنظیم
            value: مقدار جدید
        """
        segments = path.segments
        current = self.settings_data

        # Navigate to parent, creating intermediate dicts
        for segment in segments[:-1]:
            if segment not in current or not isinstance(current[segment], dict):
                current[segment] = {}
            current = current[segment]

        current[segments[-1]] = value
        self.mark_updated()

    def get_section(self, section_key: str) -> dict[str, Any]:
        """
        تمام تنظیمات یک بخش را برمی‌گرداند.

        Args:
            section_key: کلید بخش

        Returns:
            دیکشنری تنظیمات بخش
        """
        return self.settings_data.get(section_key, {})

    def set_section(self, section_key: str, data: dict[str, Any]) -> None:
        """
        تنظیمات یک بخش را کامل جایگزین می‌کند.

        Args:
            section_key: کلید بخش
            data: دیکشنری تنظیمات جدید
        """
        self.settings_data[section_key] = data
        self.mark_updated()

    def merge_defaults(self, defaults: dict[str, Any]) -> None:
        """
        مقادیر پیش‌فرض را با تنظیمات فعلی ادغام می‌کند.
        فقط فیلدهای ناموجود اضافه می‌شوند.
        """
        self._deep_merge(self.settings_data, defaults)

    @staticmethod
    def _deep_merge(target: dict, source: dict) -> None:
        """ادغام عمیق source به target — فقط کلیدهای ناموجود."""
        for key, value in source.items():
            if key not in target:
                target[key] = value
            elif isinstance(target[key], dict) and isinstance(value, dict):
                ModuleSettings._deep_merge(target[key], value)
