"""Unit tests for unflattener module."""

import json
import tempfile
import os

import pandas as pd
import pytest

from flat_fhir.unflattener import (
    unflatten_sheets, unflatten_xlsx, _val,
    _build_patient, _build_observation, _build_medication_request,
    _build_condition, _build_allergy, _build_procedure,
    _build_immunization, _build_encounter,
)


# ── _val ──

def test_val_present():
    row = pd.Series({"a": 10, "b": "hello"})
    assert _val(row, "a") == 10
    assert _val(row, "b") == "hello"


def test_val_missing():
    row = pd.Series({"a": 10})
    assert _val(row, "x") == ""
    assert _val(row, "x", "default") == "default"


def test_val_nan():
    row = pd.Series({"a": float("nan")})
    assert _val(row, "a") == ""


# ── _build_patient ──

def test_build_patient():
    row = pd.Series({
        "source_resource_id": "pat-1",
        "family_name": "Doe",
        "given_name": "John",
        "birth_date": "1990-01-01",
        "gender": "male",
        "phone": "555-1234",
        "email": "j@test.com",
        "address": "123 Main St",
    })
    result = _build_patient(row)
    assert result["resourceType"] == "Patient"
    assert result["id"] == "pat-1"
    assert result["name"][0]["family"] == "Doe"
    assert result["gender"] == "male"
    assert len(result["telecom"]) == 2
    assert result["address"][0]["text"] == "123 Main St"


def test_build_patient_no_telecom():
    row = pd.Series({
        "source_resource_id": "pat-2",
        "family_name": "Doe",
        "given_name": "Jane",
        "birth_date": "1995-01-01",
        "gender": "female",
        "phone": "",
        "email": "",
        "address": "",
    })
    result = _build_patient(row)
    assert "telecom" not in result
    assert "address" not in result


# ── _build_observation ──

def test_build_observation_vitals():
    row = pd.Series({
        "primary_key": "abc123",
        "timestamp": "2024-01-01",
        "encounter_id": "enc-1",
        "performer": "Dr. Smith",
        "source_resource_id": "obs-1",
        "notes": "",
        "heart_rate_bpm": 72,
    })
    result = _build_observation(row, "vitals")
    assert result is not None
    assert result["resourceType"] == "Observation"
    assert result["category"][0]["coding"][0]["code"] == "vital-signs"
    assert result["valueQuantity"]["value"] == 72


def test_build_observation_labs():
    row = pd.Series({
        "primary_key": "abc123",
        "timestamp": "2024-01-01",
        "encounter_id": "",
        "performer": "",
        "source_resource_id": "",
        "notes": "",
        "hba1c_pct": 6.5,
    })
    result = _build_observation(row, "labs")
    assert result is not None
    assert result["category"][0]["coding"][0]["code"] == "laboratory"


def test_build_observation_no_values():
    row = pd.Series({
        "primary_key": "abc123",
        "timestamp": "2024-01-01",
        "encounter_id": "",
        "performer": "",
        "source_resource_id": "",
        "notes": "",
    })
    result = _build_observation(row, "vitals")
    assert result is None


def test_build_observation_string_value():
    row = pd.Series({
        "primary_key": "abc123",
        "timestamp": "2024-01-01",
        "encounter_id": "",
        "performer": "",
        "source_resource_id": "",
        "notes": "",
        "some_unknown_column": "Positive",
    })
    result = _build_observation(row, "labs")
    assert result is not None
    assert result["valueString"] == "Positive"


# ── _build_medication_request ──

def test_build_medication_request():
    row = pd.Series({
        "source_resource_id": "med-1",
        "timestamp": "2024-01-01",
        "medication_name": "Metformin",
        "rxnorm_code": "860975",
        "status": "active",
        "prescriber": "Dr. Smith",
        "dose": 500,
        "dose_unit": "mg",
        "frequency": "Once daily",
        "encounter_id": "enc-1",
    })
    result = _build_medication_request(row)
    assert result["resourceType"] == "MedicationRequest"
    assert result["medicationCodeableConcept"]["coding"][0]["code"] == "860975"
    assert result["requester"]["display"] == "Dr. Smith"
    assert result["dosageInstruction"][0]["doseAndRate"][0]["doseQuantity"]["value"] == 500


def test_build_medication_no_rxnorm():
    row = pd.Series({
        "source_resource_id": "",
        "timestamp": "",
        "medication_name": "Aspirin",
        "rxnorm_code": "",
        "status": "",
        "prescriber": "",
        "dose": "",
        "dose_unit": "",
        "frequency": "",
        "encounter_id": "",
    })
    result = _build_medication_request(row)
    assert result["medicationCodeableConcept"] == {"text": "Aspirin"}


# ── _build_condition ──

def test_build_condition():
    row = pd.Series({
        "source_resource_id": "cond-1",
        "timestamp": "2024-01-01",
        "condition_name": "Diabetes",
        "icd_code": "E11.9",
        "snomed_code": "44054006",
        "clinical_status": "active",
        "onset_date": "2020-01-01",
        "abatement_date": "",
    })
    result = _build_condition(row)
    assert result["resourceType"] == "Condition"
    assert len(result["code"]["coding"]) == 2
    assert result["clinicalStatus"]["coding"][0]["code"] == "active"
    assert result["onsetDateTime"] == "2020-01-01"
    assert "abatementDateTime" not in result


# ── _build_allergy ──

def test_build_allergy():
    row = pd.Series({
        "source_resource_id": "allergy-1",
        "substance": "Penicillin",
        "criticality": "high",
        "reaction": "Rash",
        "severity": "severe",
        "timestamp": "2020-01-01",
    })
    result = _build_allergy(row)
    assert result["resourceType"] == "AllergyIntolerance"
    assert result["code"]["text"] == "Penicillin"
    assert result["criticality"] == "high"
    assert result["reaction"][0]["manifestation"][0]["text"] == "Rash"
    assert result["reaction"][0]["severity"] == "severe"


def test_build_allergy_no_reaction():
    row = pd.Series({
        "source_resource_id": "",
        "substance": "Latex",
        "criticality": "",
        "reaction": "",
        "severity": "",
        "timestamp": "",
    })
    result = _build_allergy(row)
    assert "reaction" not in result


# ── _build_procedure ──

def test_build_procedure():
    row = pd.Series({
        "source_resource_id": "proc-1",
        "timestamp": "2024-01-15",
        "procedure_name": "ECG",
        "snomed_code": "29303009",
        "body_site": "Chest",
        "outcome": "Normal",
        "status": "completed",
    })
    result = _build_procedure(row)
    assert result["resourceType"] == "Procedure"
    assert result["code"]["coding"][0]["code"] == "29303009"
    assert result["bodySite"][0]["text"] == "Chest"
    assert result["outcome"]["text"] == "Normal"


def test_build_procedure_text_only():
    row = pd.Series({
        "source_resource_id": "",
        "timestamp": "",
        "procedure_name": "Physical Exam",
        "snomed_code": "",
        "body_site": "",
        "outcome": "",
        "status": "",
    })
    result = _build_procedure(row)
    assert result["code"] == {"text": "Physical Exam"}
    assert "bodySite" not in result


# ── _build_immunization ──

def test_build_immunization():
    row = pd.Series({
        "source_resource_id": "imm-1",
        "timestamp": "2024-10-01",
        "vaccine_name": "Influenza",
        "cvx_code": "140",
        "lot_number": "LOT123",
        "site": "Left arm",
        "dose": 0.5,
        "dose_unit": "mL",
        "status": "completed",
    })
    result = _build_immunization(row)
    assert result["resourceType"] == "Immunization"
    assert result["vaccineCode"]["coding"][0]["code"] == "140"
    assert result["lotNumber"] == "LOT123"
    assert result["site"]["text"] == "Left arm"
    assert result["doseQuantity"]["value"] == 0.5


def test_build_immunization_text_only():
    row = pd.Series({
        "source_resource_id": "",
        "timestamp": "",
        "vaccine_name": "COVID-19",
        "cvx_code": "",
        "lot_number": "",
        "site": "",
        "dose": "",
        "dose_unit": "",
        "status": "",
    })
    result = _build_immunization(row)
    assert result["vaccineCode"] == {"text": "COVID-19"}
    assert "lotNumber" not in result


# ── _build_encounter ──

def test_build_encounter():
    row = pd.Series({
        "source_resource_id": "enc-1",
        "encounter_id": "enc-1",
        "timestamp": "2024-01-01",
        "performer": "Dr. Jones",
        "type": "Office Visit",
        "status": "finished",
        "facility": "Springfield Clinic",
        "period_start": "2024-01-01T09:00:00Z",
        "period_end": "2024-01-01T10:00:00Z",
        "reason": "Annual checkup; Follow-up",
        "discharge_disposition": "Home",
    })
    result = _build_encounter(row)
    assert result["resourceType"] == "Encounter"
    assert result["type"][0]["text"] == "Office Visit"
    assert result["period"]["start"] == "2024-01-01T09:00:00Z"
    assert result["serviceProvider"]["display"] == "Springfield Clinic"
    assert result["participant"][0]["individual"]["display"] == "Dr. Jones"
    assert len(result["reasonCode"]) == 2
    assert result["hospitalization"]["dischargeDisposition"]["text"] == "Home"


def test_build_encounter_minimal():
    row = pd.Series({
        "source_resource_id": "",
        "encounter_id": "",
        "timestamp": "",
        "performer": "",
        "type": "",
        "status": "",
        "facility": "",
        "period_start": "",
        "period_end": "",
        "reason": "",
        "discharge_disposition": "",
    })
    result = _build_encounter(row)
    assert result["resourceType"] == "Encounter"
    assert "type" not in result
    assert "serviceProvider" not in result


# ── unflatten_sheets ──

def test_unflatten_sheets_empty():
    sheets = {}
    bundle = unflatten_sheets(sheets)
    assert bundle["resourceType"] == "Bundle"
    assert len(bundle["entry"]) == 0


def test_unflatten_sheets_full():
    sheets = {
        "patient": pd.DataFrame([{
            "primary_key": "pk1", "source_resource_id": "pat-1",
            "family_name": "Doe", "given_name": "John",
            "birth_date": "1990-01-01", "gender": "male",
            "phone": "", "email": "", "address": "",
        }]),
        "vitals": pd.DataFrame([{
            "primary_key": "pk2", "timestamp": "2024-01-01",
            "encounter_id": "", "performer": "", "source_resource_id": "",
            "notes": "", "heart_rate_bpm": 72,
        }]),
        "conditions": pd.DataFrame([{
            "primary_key": "pk3", "timestamp": "2024-01-01",
            "source_resource_id": "cond-1", "condition_name": "Diabetes",
            "icd_code": "E11.9", "snomed_code": "", "clinical_status": "active",
            "onset_date": "", "abatement_date": "",
        }]),
    }
    bundle = unflatten_sheets(sheets)
    assert len(bundle["entry"]) == 3
    rtypes = {e["resource"]["resourceType"] for e in bundle["entry"]}
    assert "Patient" in rtypes
    assert "Observation" in rtypes
    assert "Condition" in rtypes


# ── unflatten_xlsx ──

def test_build_condition_text_only():
    """Condition with name but no ICD/SNOMED codes."""
    row = pd.Series({
        "source_resource_id": "",
        "timestamp": "",
        "condition_name": "Headache",
        "icd_code": "",
        "snomed_code": "",
        "clinical_status": "",
        "onset_date": "",
        "abatement_date": "",
    })
    result = _build_condition(row)
    assert result["code"] == {"text": "Headache"}


def test_unflatten_xlsx_round_trip(tmp_path):
    from flat_fhir import FlatFHIR, flatten
    from pathlib import Path

    sample = Path(__file__).parent / "fixtures" / "sample_bundle.json"
    xlsx = str(tmp_path / "test.xlsx")
    flatten(str(sample), output=xlsx)

    bundle = unflatten_xlsx(xlsx)
    assert bundle["resourceType"] == "Bundle"
    assert len(bundle["entry"]) > 0
