from __future__ import annotations

import re
from collections.abc import Mapping, MutableMapping
from dataclasses import dataclass, field
from threading import RLock
from typing import Any

from structly import FieldPatternType


@dataclass
class DomainPatternRegistry:
    """Thread-safe registry of prefix/regex patterns used to infer domains from raw WHOIS text."""

    prefixes: tuple[str, ...] = ()
    regexes: tuple[re.Pattern[str], ...] = ()
    _lock: RLock = field(default_factory=RLock, init=False, repr=False)

    def refresh(
        self,
        base_fields: Mapping[str, MutableMapping[str, Any]],
        overrides: Mapping[str, Mapping[str, MutableMapping[str, Any]]],
    ) -> None:
        prefixes: list[str] = []
        regexes: list[str] = []
        seen_prefixes: set[str] = set()
        seen_regexes: set[str] = set()

        def _add_patterns(field_def: MutableMapping[str, Any] | None) -> None:
            if not field_def:
                return
            for key in ("patterns", "extend_patterns", "prepend_patterns"):
                patterns = field_def.get(key)
                if not patterns:
                    continue
                for pattern in patterns:
                    if pattern.pattern_type == FieldPatternType.STARTS_WITH:
                        if pattern.pattern in seen_prefixes:
                            continue
                        seen_prefixes.add(pattern.pattern)
                        prefixes.append(pattern.pattern)
                    elif pattern.pattern_type == FieldPatternType.REGEX:
                        if pattern.pattern in seen_regexes:
                            continue
                        seen_regexes.add(pattern.pattern)
                        regexes.append(pattern.pattern)

        _add_patterns(base_fields.get("domain_name"))
        for domain_overrides in overrides.values():
            _add_patterns(domain_overrides.get("domain_name"))

        compiled = tuple(re.compile(p, re.MULTILINE) for p in regexes)
        with self._lock:
            self.prefixes = tuple(prefixes)
            self.regexes = compiled

    def infer(self, text: str) -> str | None:
        """Return the first domain-like string extracted from the given text, or None if nothing matches."""
        for pattern in self.regexes:
            match = pattern.search(text)
            if not match:
                continue
            group_dict = match.groupdict()
            for key in ("domain", "val", "value"):
                candidate = group_dict.get(key)
                if candidate:
                    return candidate.strip()
            if match.lastindex:
                candidate = match.group(match.lastindex)
                if candidate:
                    return candidate.strip()
            candidate = match.group(0).strip()
            if candidate:
                return candidate
        for line in text.splitlines():
            for prefix in self.prefixes:
                if line.startswith(prefix):
                    remainder = line[len(prefix) :].strip()
                    if not remainder:
                        continue
                    candidate = remainder.split()[0].strip(" .")
                    if candidate:
                        return candidate
        return None


_REGISTRY = DomainPatternRegistry()


def refresh_domain_markers(
    base_fields: Mapping[str, MutableMapping[str, Any]],
    overrides: Mapping[str, Mapping[str, MutableMapping[str, Any]]],
) -> None:
    """Rebuild the shared pattern registry from the current Structly base fields and TLD overrides."""
    _REGISTRY.refresh(base_fields, overrides)


def normalise_tld(label: str | None) -> str:
    if not label:
        return ""
    return label.strip().lstrip(".").lower()


def split_domain(domain: str | None) -> list[str]:
    if not domain:
        return []
    stripped = domain.strip().strip(".").lower()
    return [segment for segment in stripped.split(".") if segment]


def infer_domain_from_text(text: str) -> str | None:
    """Best-effort domain inference when a WHOIS payload lacks an explicit domain field."""
    return _REGISTRY.infer(text)


def get_domain_registry() -> DomainPatternRegistry:
    """Expose the global DomainPatternRegistry for inspection/testing."""
    return _REGISTRY


__all__ = [
    "infer_domain_from_text",
    "normalise_tld",
    "split_domain",
    "DomainPatternRegistry",
    "refresh_domain_markers",
    "get_domain_registry",
]
