"""RUCAM 7-domain scoring + CIOMS R-ratio + Naranjo + Maria-Victorino.

근거: Danan & Benichou 1993 (RUCAM), Aithal 2011 (DILI causality consensus),
AASLD/IDILI causality (참고용 텍스트). 본 모듈은 자동화 보조이며 임상 판단을 대체하지 않는다.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, List, Optional

from .hys_law import HysCase


# -------------------- RUCAM 7 domains --------------------
# 각 도메인은 -3 ~ +3 범위.
RUCAM_DOMAINS = [
    "temporal_relation",
    "course_after_cessation",
    "risk_factors",
    "concomitant_drugs",
    "alternative_causes",
    "previous_information_on_drug",
    "response_to_readministration",
]


@dataclass
class RUCAMInput:
    pid: str
    arm: str
    drug_class: str
    # 1. temporal
    time_to_onset_days: Optional[int] = None
    rechallenge: bool = False
    # 2. course
    alt_decline_pct_30d: Optional[float] = None  # %
    cholestatic_pattern: bool = False
    # 3. risk
    age_over_55: bool = False
    pregnancy: bool = False
    alcohol: bool = False
    # 4. concomitant
    n_concomitant_known_hepatotoxic: int = 0
    # 5. alternative cause
    hbv_hcv_ruled_out: bool = True
    biliary_obstruction_ruled_out: bool = True
    autoimmune_ruled_out: bool = True
    ischemia_ruled_out: bool = True
    # 6. known info
    label_lists_dili: bool = False
    case_reports_exist: bool = False
    # 7. response to readmin
    positive_rechallenge_alt_doubled: bool = False
    # extras for Naranjo
    placebo_response: Optional[bool] = None
    objective_evidence: bool = True


@dataclass
class RUCAMResult:
    pid: str
    domain_scores: Dict[str, int] = field(default_factory=dict)
    total: int = 0
    category: str = "unassessable"
    cioms_r_ratio: Optional[float] = None
    pattern: str = "indeterminate"
    naranjo: Optional[int] = None
    naranjo_category: Optional[str] = None
    maria_victorino: Optional[int] = None


def _score_temporal(inp: RUCAMInput) -> int:
    """Hepatocellular onset 5~90d: +2, <5 or >90d: +1, beyond 15d after stop: 0."""
    if inp.time_to_onset_days is None:
        return 0
    t = inp.time_to_onset_days
    if 5 <= t <= 90:
        return 2
    if t < 5 or t > 90:
        return 1
    return 0


def _score_course(inp: RUCAMInput) -> int:
    """ALT 30일내 50% 이상 하강: +2 / 30~50%: +1 / else 0."""
    if inp.alt_decline_pct_30d is None:
        return 0
    d = inp.alt_decline_pct_30d
    if d >= 50:
        return 2
    if d >= 30:
        return 1
    return 0


def _score_risk(inp: RUCAMInput) -> int:
    s = 0
    if inp.age_over_55:
        s += 1
    if inp.alcohol or inp.pregnancy:
        s += 1
    return min(s, 2)


def _score_concomitant(inp: RUCAMInput) -> int:
    n = inp.n_concomitant_known_hepatotoxic
    if n == 0:
        return 0
    if n == 1:
        return -1
    if n == 2:
        return -2
    return -3


def _score_alternative(inp: RUCAMInput) -> int:
    ruled = sum([
        inp.hbv_hcv_ruled_out,
        inp.biliary_obstruction_ruled_out,
        inp.autoimmune_ruled_out,
        inp.ischemia_ruled_out,
    ])
    if ruled == 4:
        return 2
    if ruled == 3:
        return 1
    if ruled == 2:
        return 0
    if ruled == 1:
        return -2
    return -3


def _score_known(inp: RUCAMInput) -> int:
    if inp.label_lists_dili:
        return 2
    if inp.case_reports_exist:
        return 1
    return 0


def _score_rechallenge(inp: RUCAMInput) -> int:
    if not inp.rechallenge:
        return 0
    return 3 if inp.positive_rechallenge_alt_doubled else -2


def score_rucam(inp: RUCAMInput) -> RUCAMResult:
    res = RUCAMResult(pid=inp.pid)
    res.domain_scores["temporal_relation"] = _score_temporal(inp)
    res.domain_scores["course_after_cessation"] = _score_course(inp)
    res.domain_scores["risk_factors"] = _score_risk(inp)
    res.domain_scores["concomitant_drugs"] = _score_concomitant(inp)
    res.domain_scores["alternative_causes"] = _score_alternative(inp)
    res.domain_scores["previous_information_on_drug"] = _score_known(inp)
    res.domain_scores["response_to_readministration"] = _score_rechallenge(inp)
    res.total = sum(res.domain_scores.values())
    res.category = categorize(res.total)
    return res


def categorize(total: int) -> str:
    if total > 8:
        return "highly_probable"
    if total >= 6:
        return "probable"
    if total >= 3:
        return "possible"
    if total >= 1:
        return "unlikely"
    return "excluded"


# -------------------- Naranjo --------------------
NARANJO_QUESTIONS = [
    "previous_conclusive_reports",
    "appeared_after_drug",
    "improved_after_discontinuation",
    "reappeared_on_rechallenge",
    "alternative_causes",
    "appeared_with_placebo",
    "toxic_drug_level_detected",
    "dose_response",
    "previous_similar_reaction",
    "confirmed_by_objective_evidence",
]


def score_naranjo(inp: RUCAMInput) -> int:
    s = 0
    s += 1 if inp.case_reports_exist or inp.label_lists_dili else 0
    s += 2 if inp.time_to_onset_days is not None and inp.time_to_onset_days >= 1 else 0
    s += 1 if (inp.alt_decline_pct_30d or 0) >= 30 else 0
    s += 2 if inp.rechallenge and inp.positive_rechallenge_alt_doubled else 0
    ruled = sum([inp.hbv_hcv_ruled_out, inp.biliary_obstruction_ruled_out,
                 inp.autoimmune_ruled_out, inp.ischemia_ruled_out])
    s += 2 if ruled == 4 else (1 if ruled == 3 else 0)
    s += -1 if inp.placebo_response else 0
    s += 0   # toxic drug level not applicable
    s += 0   # dose-response not captured
    s += 1 if inp.objective_evidence else 0
    return s


def naranjo_category(score: int) -> str:
    if score >= 9:
        return "definite"
    if score >= 5:
        return "probable"
    if score >= 1:
        return "possible"
    return "doubtful"


# -------------------- Maria-Victorino (간이) --------------------
def score_maria_victorino(inp: RUCAMInput) -> int:
    s = 0
    if inp.time_to_onset_days is not None and 5 <= inp.time_to_onset_days <= 90:
        s += 3
    elif inp.time_to_onset_days is not None:
        s += 1
    if (inp.alt_decline_pct_30d or 0) >= 50:
        s += 3
    if inp.hbv_hcv_ruled_out and inp.biliary_obstruction_ruled_out and \
       inp.autoimmune_ruled_out and inp.ischemia_ruled_out:
        s += 3
    if inp.label_lists_dili:
        s += 3
    if inp.rechallenge and inp.positive_rechallenge_alt_doubled:
        s += 3
    return s


# -------------------- batch evaluate --------------------
def evaluate_batch(inputs: List[RUCAMInput], cases: List[HysCase]) -> List[RUCAMResult]:
    case_map = {c.pid: c for c in cases}
    out: List[RUCAMResult] = []
    for inp in inputs:
        res = score_rucam(inp)
        c = case_map.get(inp.pid)
        if c is not None:
            res.cioms_r_ratio = c.r_ratio
            res.pattern = c.pattern
        res.naranjo = score_naranjo(inp)
        res.naranjo_category = naranjo_category(res.naranjo)
        res.maria_victorino = score_maria_victorino(inp)
        out.append(res)
    return out


def derive_inputs_from_cases(cases: List[HysCase]) -> List[RUCAMInput]:
    """eDISH case에서 RUCAM input의 합리적 default를 생성 (자동 1차 패스용)."""
    inputs: List[RUCAMInput] = []
    for c in cases:
        # 시그널 강도에 따라 derived defaults
        positive_signal = c.classical_hys or c.baseline_adj_hys
        inp = RUCAMInput(
            pid=c.pid, arm=c.arm, drug_class=c.drug_class,
            time_to_onset_days=45 if positive_signal else 120,
            alt_decline_pct_30d=55 if positive_signal else 10,
            rechallenge=False,
            age_over_55=False,
            alcohol=False,
            n_concomitant_known_hepatotoxic=0,
            hbv_hcv_ruled_out=True,
            biliary_obstruction_ruled_out=True,
            autoimmune_ruled_out=True,
            ischemia_ruled_out=True,
            label_lists_dili=False,
            case_reports_exist=positive_signal,
            positive_rechallenge_alt_doubled=False,
            placebo_response=(c.arm.lower() == "placebo"),
            objective_evidence=positive_signal,
        )
        inputs.append(inp)
    return inputs
