# retoor <retoor@molodetz.nl>

from __future__ import annotations

import logging
from dataclasses import dataclass

from devplacepy.database import get_int_setting, get_setting

from devplacepy.services.moderation.rules import (
    ALWAYS_BLOCK_CATEGORIES,
    DEFAULT_REVIEW_SCORE,
    FILTER_MODES,
    MATURE_CATEGORIES,
    matches,
    score_for,
)

logger = logging.getLogger(__name__)

VERDICT_ORDER: dict[str, int] = {"allow": 0, "label": 1, "review": 2, "block": 3}

MODE_ORDER: dict[str, int] = {"off": 0, "label": 1, "review": 2, "block": 3}


@dataclass(frozen=True)
class Classification:
    verdict: str = "allow"
    categories: tuple[str, ...] = ()
    maturity: str = "general"
    score: int = 0
    failed: bool = False
    detail: str = ""

    @property
    def flagged(self) -> bool:
        return self.verdict in ("review", "block")


@dataclass(frozen=True)
class Screening:
    classification: Classification
    fields: tuple[str, ...] = ()

    @property
    def blocked(self) -> bool:
        return self.classification.verdict == "block"

    @property
    def flagged(self) -> bool:
        return self.classification.flagged


def filter_mode() -> str:
    mode = get_setting("moderation_filter_mode", "review")
    return mode if mode in FILTER_MODES else "review"


def review_score() -> int:
    return max(
        1, get_int_setting("moderation_filter_review_score", DEFAULT_REVIEW_SCORE)
    )


def resolve_verdict(verdict: str, mode: str) -> str:
    if mode == "off":
        return "allow"
    if mode == "label":
        return "label" if VERDICT_ORDER[verdict] > VERDICT_ORDER["label"] else verdict
    if mode == "block" and verdict == "review":
        return "block"
    return verdict


def classify(text: str, mode: str = "") -> Classification:
    active = mode if mode in FILTER_MODES else filter_mode()
    if active == "off" or not (text or "").strip():
        return Classification()
    try:
        return _classify(text, active)
    except Exception as exc:
        logger.warning("moderation filter failed, falling back to review: %s", exc)
        return Classification(
            verdict="review",
            categories=("other",),
            failed=True,
            detail=f"classification failed: {exc}",
        )


def _classify(text: str, mode: str) -> Classification:
    matched = matches(text)
    if not matched:
        return Classification()
    categories = tuple(sorted({rule.category for rule in matched}))
    score = score_for(text, matched)
    if score <= 0:
        return Classification(categories=categories)
    if set(categories) & ALWAYS_BLOCK_CATEGORIES:
        verdict = "block"
    elif score >= review_score():
        verdict = "review"
    else:
        verdict = "label"
    maturity = "mature" if set(categories) & MATURE_CATEGORIES else "general"
    return Classification(
        verdict=resolve_verdict(verdict, mode),
        categories=categories,
        maturity=maturity,
        score=score,
        detail=", ".join(categories),
    )


def screen(values: dict[str, str], mode: str = "") -> Screening:
    worst = Classification()
    flagged_fields: list[str] = []
    for name, value in values.items():
        result = classify(value or "", mode)
        if result.verdict == "allow":
            continue
        flagged_fields.append(name)
        if VERDICT_ORDER[result.verdict] > VERDICT_ORDER[worst.verdict] or (
            result.verdict == worst.verdict and result.score > worst.score
        ):
            worst = result
    return Screening(classification=worst, fields=tuple(flagged_fields))
