"""Canonical entity resolution for financial risk graph subjects."""

from __future__ import annotations

import hashlib
import re
from dataclasses import dataclass, field
from datetime import UTC, datetime
from typing import Any


ENTITY_TYPE_ALIASES = {
    "supplier": "vendor",
    "merchant": "vendor",
    "hotel": "vendor",
    "bank_account_name": "bank_account",
    "employee_name": "employee",
}


@dataclass(slots=True)
class CanonicalEntity:
    canonical_id: str
    entity_type: str
    canonical_key: str
    label: str
    aliases: list[str] = field(default_factory=list)
    source: str = ""
    confirmed_by: str = ""
    confirmed_at: str = ""
    metadata: dict[str, Any] = field(default_factory=dict)

    def as_dict(self) -> dict[str, Any]:
        return {
            "canonical_id": self.canonical_id,
            "entity_type": self.entity_type,
            "canonical_key": self.canonical_key,
            "label": self.label,
            "aliases": list(self.aliases),
            "source": self.source,
            "confirmed_by": self.confirmed_by,
            "confirmed_at": self.confirmed_at,
            "metadata": dict(self.metadata),
        }


class FinancialEntityResolver:
    def resolve(
        self,
        entity_type: str,
        value: str,
        *,
        source: str = "",
        metadata: dict[str, Any] | None = None,
    ) -> CanonicalEntity | None:
        canonical_type = ENTITY_TYPE_ALIASES.get(_canonical_token(entity_type), _canonical_token(entity_type))
        canonical_key = _canonical_value(value)
        if not canonical_type or not canonical_key:
            return None
        canonical_id = _canonical_id(canonical_type, canonical_key)
        return CanonicalEntity(
            canonical_id=canonical_id,
            entity_type=canonical_type,
            canonical_key=canonical_key,
            label=str(value or "").strip(),
            aliases=[str(value or "").strip()],
            source=source,
            metadata=metadata or {},
        )


class CanonicalEntityRegistry:
    def __init__(self) -> None:
        self._entities: dict[str, CanonicalEntity] = {}

    def upsert(self, entity: CanonicalEntity) -> CanonicalEntity:
        current = self._entities.get(entity.canonical_id)
        if current is None:
            self._entities[entity.canonical_id] = entity
            return entity
        aliases = list(dict.fromkeys([*current.aliases, *entity.aliases]))
        current.aliases = aliases
        current.metadata.update(entity.metadata)
        return current

    def confirm(self, canonical_id: str, *, actor: str) -> CanonicalEntity | None:
        entity = self._entities.get(canonical_id)
        if entity is None:
            return None
        entity.confirmed_by = str(actor or "").strip()
        entity.confirmed_at = datetime.now(UTC).isoformat()
        return entity

    def get(self, canonical_id: str) -> CanonicalEntity | None:
        return self._entities.get(canonical_id)

    def all(self) -> list[CanonicalEntity]:
        return list(self._entities.values())


def _canonical_id(entity_type: str, canonical_key: str) -> str:
    digest = hashlib.sha1(f"{entity_type}:{canonical_key}".encode("utf-8")).hexdigest()[:12]
    return f"{entity_type}:{digest}"


def _canonical_token(value: str) -> str:
    return "_".join(str(value or "").strip().lower().split())


def _canonical_value(value: str) -> str:
    normalized = str(value or "").strip().lower()
    normalized = re.sub(r"[\s\-_/,，.。()（）【】\[\]]+", "", normalized)
    return normalized
