# Copyright (C) 2026 NPO NT
# SPDX-License-Identifier: AGPL-3.0-only

from __future__ import annotations

import pytest

from mika_deid.detectors import detect_tokens
from mika_deid.models import Box, WordToken


def tokens_for_line(text: str, line: int = 0) -> list[WordToken]:
    tokens: list[WordToken] = []
    x = 10
    for word in text.split():
        width = max(12, len(word) * 7)
        tokens.append(
            WordToken(
                text=word,
                box=Box(x, 10, x + width, 24),
                line_key=(0, 0, line),
                confidence=99.0,
                source="test",
            )
        )
        x += width + 6
    return tokens


def categories(text: str) -> set[str]:
    return {finding.category for finding in detect_tokens(tokens_for_line(text), 0)}


def test_direct_identifier_rules() -> None:
    assert "person_name" in categories("Ф.И.О.: Иванов Иван Иванович")
    assert "birth_date" in categories("Дата рождения: 12.04.1978")
    assert "phone" in categories("Телефон: +7 999 123-45-67")
    assert "email" in categories("Email: synthetic.patient@example.test")
    assert "snils" in categories("СНИЛС: 123-456-789 01")
    assert "insurance_policy" in categories("Полис ОМС: 1234567890123456")
    assert "medical_record_id" in categories("Медицинская карта: MRN-7742-A")
    assert "address" in categories("Адрес проживания: г. Тестоград, улица Примерная, дом 7")
    assert "email" in categories("Email: зупИе#с.ранеп{@ехатр!е4{ез!")


def test_unlabelled_patronymic_name_is_detected() -> None:
    assert "person_name" in categories("Иванов Иван Иванович")


@pytest.mark.parametrize(
    "text",
    [
        "Фамилия: Иванов",
        "ФИО пациента: Иванов Иван Иванович",
        "Ф.И.О. пациента: Иванов И. И.",
        "Пациент: Иванов И.И.",
        "Иванов И.И.",
        "ИВАНОВ И.И.",
    ],
)
def test_labelled_patient_names_are_selected_for_redaction(text: str) -> None:
    names = [finding for finding in detect_tokens(tokens_for_line(text), 0) if finding.category == "person_name"]
    assert names
    assert all(finding.action.value == "redact" and finding.selected for finding in names)


def test_event_date_is_review_not_birth_date() -> None:
    findings = detect_tokens(tokens_for_line("Биопсия выполнена 15.08.2026"), 0)
    event = next(finding for finding in findings if finding.category == "event_date")
    assert event.action.value == "review"
    assert not event.selected


@pytest.mark.parametrize(
    "text",
    [
        "Дата операции: 15.08.2026",
        "Анализ выполнен 15.08.2026",
        "Дата исследования: 15.08.2026",
        "Забор материала 15.08.2026",
        "Госпитализация 15.08.2026",
        "Начало терапии 15.08.2026",
        "По данным PSA от 15.08.2026",
        "ПСА от 15.08.2026",
    ],
)
def test_clinical_dates_are_preserved_by_default(text: str) -> None:
    event = next(
        finding
        for finding in detect_tokens(tokens_for_line(text), 0)
        if finding.category == "event_date"
    )
    assert event.action.value == "review"
    assert not event.selected


def test_birth_date_remains_selected_for_redaction() -> None:
    findings = detect_tokens(tokens_for_line("Дата рождения: 12.04.1978"), 0)
    birth_date = next(finding for finding in findings if finding.category == "birth_date")
    assert birth_date.action.value == "redact"
    assert birth_date.selected
    assert all(finding.category != "event_date" for finding in findings)


@pytest.mark.parametrize(
    "text",
    [
        "Дата рожд.: 12.04.1978",
        "Д.р. 12 / 04 / 1978",
        "DOB: 1978-04-12",
        "Год рождения: 1978/04/12",
        "Дата рождения/ Birth date: 12.04.1978",
        "Дата рождения/ Birth date: 12.04.1978r Возраст/Age: 48 лет",
    ],
)
def test_birth_date_context_is_selected_across_common_formats(text: str) -> None:
    findings = detect_tokens(tokens_for_line(text), 0)
    birth_dates = [finding for finding in findings if finding.category == "birth_date"]
    assert birth_dates
    assert all(finding.selected for finding in birth_dates)
    assert all(finding.category != "event_date" for finding in findings)


def test_public_finding_never_exports_detected_text() -> None:
    finding = detect_tokens(tokens_for_line("Email: secret@example.test"), 0)[0]
    public = finding.public_dict()
    serialized = str(public)
    assert "secret@example.test" not in serialized
    assert "text" not in public
