"""Immutable value objects used by external data-source services."""

import hashlib
import json
from dataclasses import dataclass, field, replace
from types import MappingProxyType
from typing import Mapping, Optional


@dataclass(frozen=True)
class DataSourceCredential:
    username: str
    password: str = field(repr=False)
    options: Mapping[str, str] = field(default_factory=dict)

    def __post_init__(self):
        object.__setattr__(
            self,
            "options",
            MappingProxyType(dict(self.options)),
        )


@dataclass(frozen=True)
class SealedCredential:
    id: str
    data_source_uid: str
    credential_version: int
    encrypted_payload: bytes = field(repr=False)
    nonce: bytes = field(repr=False)
    key_version: str
    status: str = "active"


@dataclass(frozen=True)
class DataSourceDefinition:
    uid: Optional[str]
    name_en: str
    database_type: str
    host: str
    port: int
    database: str
    name_zh: Optional[str] = None
    schema: Optional[str] = None
    credential_ref: Optional[str] = None
    credential_version: Optional[int] = None
    pool_size: Optional[int] = None
    max_overflow: Optional[int] = None
    tls_options: Mapping[str, str] = field(default_factory=dict)
    status: bool = True
    description: Optional[str] = None
    extra_properties: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self):
        object.__setattr__(
            self,
            "database_type",
            str(self.database_type).strip().lower(),
        )
        object.__setattr__(self, "port", int(self.port))
        object.__setattr__(
            self,
            "tls_options",
            MappingProxyType(dict(self.tls_options)),
        )
        object.__setattr__(
            self,
            "extra_properties",
            MappingProxyType(dict(self.extra_properties)),
        )

    def with_uid(self, uid: str):
        return replace(self, uid=uid)

    def pool_overrides(self) -> dict:
        return {
            key: value
            for key, value in {
                "pool_size": self.pool_size,
                "max_overflow": self.max_overflow,
            }.items()
            if value is not None
        }

    def connection_fingerprint(self) -> str:
        payload = {
            "database_type": self.database_type,
            "host": self.host,
            "port": self.port,
            "database": self.database,
            "schema": self.schema,
            "pool_overrides": self.pool_overrides(),
            "tls_options": dict(self.tls_options),
        }
        canonical = json.dumps(
            payload,
            sort_keys=True,
            separators=(",", ":"),
        )
        return hashlib.sha256(canonical.encode("utf-8")).hexdigest()


@dataclass(frozen=True)
class PoolKey:
    data_source_uid: str
    credential_version: int
    config_fingerprint: str


@dataclass(frozen=True)
class PoolStatus:
    data_source_uid: str
    credential_version: int
    config_fingerprint: str
    pool_state: str
    leases: int
    draining: bool
    created_at: float
    last_used_at: float
    pool_size: int = 0
    checked_out: int = 0
    checked_out_peak: int = 0
    checked_in: int = 0
    overflow: int = 0
    checkout_wait_ms: float = 0.0
    connection_created_total: int = 0
    connection_failed_total: int = 0
    pool_timeout_total: int = 0
    invalidated_total: int = 0
    query_total: int = 0
    last_query_duration_ms: float = 0.0
    consecutive_failures: int = 0
    circuit_open_until: float = 0.0
