"""Unit tests for core module — covers edge cases and code paths."""

import json
from pathlib import Path

import pytest

from flat_fhir.core import FlatFHIR, _make_primary_key, _pivot_observations, SHEET_NAMES
import pandas as pd


FIXTURES = Path(__file__).parent / "fixtures"
SAMPLE = FIXTURES / "sample_bundle.json"


# ── FlatFHIR init ──

def test_init_from_file():
    ff = FlatFHIR(str(SAMPLE))
    assert "patient" in ff.sheets()


def test_init_from_dict():
    with open(SAMPLE, encoding="utf-8") as f:
        data = json.load(f)
    ff = FlatFHIR(data)
    assert "patient" in ff.sheets()


def test_init_from_list_of_dicts():
    with open(SAMPLE, encoding="utf-8") as f:
        data = json.load(f)
    ff = FlatFHIR([data, data])
    assert "patient" in ff.sheets()


def test_init_from_list_of_paths():
    ff = FlatFHIR([str(SAMPLE), str(SAMPLE)])
    assert "patient" in ff.sheets()


def test_init_single_resource():
    """A single resource (not a bundle) should be handled."""
    patient = {
        "resourceType": "Patient",
        "id": "single-1",
        "name": [{"family": "Solo", "given": ["Han"]}],
        "birthDate": "1980-01-01",
        "gender": "male",
    }
    ff = FlatFHIR(patient)
    assert "patient" in ff.sheets()
    assert ff.patient.iloc[0]["family_name"] == "Solo"


# ── __getattr__ ──

def test_getattr_valid_sheet():
    ff = FlatFHIR(str(SAMPLE))
    assert not ff.patient.empty


def test_getattr_invalid():
    ff = FlatFHIR(str(SAMPLE))
    with pytest.raises(AttributeError):
        _ = ff.nonexistent_attribute


def test_getattr_empty_sheet():
    ff = FlatFHIR(str(SAMPLE))
    # imaging is likely empty in sample bundle
    df = ff.imaging
    assert df.empty


# ── sheets() ──

def test_sheets_returns_non_empty():
    ff = FlatFHIR(str(SAMPLE))
    sheets = ff.sheets()
    assert all(s in SHEET_NAMES for s in sheets)
    assert len(sheets) > 0


# ── _route_resource skip non-clinical ──

def test_skips_non_clinical_resources():
    bundle = {
        "resourceType": "Bundle",
        "entry": [
            {"resource": {"resourceType": "Organization", "id": "org-1"}},
            {"resource": {"resourceType": "Claim", "id": "claim-1"}},
            {"resource": {"resourceType": "Coverage", "id": "cov-1"}},
            {"resource": {"resourceType": "Patient", "id": "p-1",
                          "name": [{"family": "Test"}], "gender": "other"}},
        ],
    }
    ff = FlatFHIR(bundle)
    assert "patient" in ff.sheets()
    assert len(ff.patient) == 1


def test_handles_malformed_resource():
    """Malformed resources should be skipped without crashing."""
    bundle = {
        "resourceType": "Bundle",
        "entry": [
            {"resource": {"resourceType": "Observation"}},  # Missing required fields
            {"resource": {"resourceType": "Patient", "id": "p-1",
                          "name": [{"family": "OK"}], "gender": "female"}},
        ],
    }
    ff = FlatFHIR(bundle)
    assert "patient" in ff.sheets()


def test_contained_resources():
    """Contained resources should be processed."""
    bundle = {
        "resourceType": "Bundle",
        "entry": [{
            "resource": {
                "resourceType": "Encounter",
                "id": "enc-1",
                "status": "finished",
                "contained": [{
                    "resourceType": "Condition",
                    "id": "cond-contained",
                    "code": {"text": "Headache"},
                    "clinicalStatus": {"coding": [{"code": "active"}]},
                }],
            },
        }],
    }
    ff = FlatFHIR(bundle)
    assert "conditions" in ff.sheets()


# ── MedicationStatement and MedicationAdministration ──

def test_medication_statement():
    bundle = {
        "resourceType": "Bundle",
        "entry": [{
            "resource": {
                "resourceType": "MedicationStatement",
                "id": "ms-1",
                "status": "active",
                "medicationCodeableConcept": {"text": "Ibuprofen"},
            },
        }],
    }
    ff = FlatFHIR(bundle)
    assert "medications" in ff.sheets()
    assert len(ff.medications) == 1


def test_medication_administration():
    bundle = {
        "resourceType": "Bundle",
        "entry": [{
            "resource": {
                "resourceType": "MedicationAdministration",
                "id": "ma-1",
                "status": "completed",
                "medicationCodeableConcept": {"text": "Morphine"},
            },
        }],
    }
    ff = FlatFHIR(bundle)
    assert "medications" in ff.sheets()


# ── Output methods ──

def test_to_xlsx(tmp_path):
    ff = FlatFHIR(str(SAMPLE))
    out = str(tmp_path / "test.xlsx")
    ff.to_xlsx(out)
    assert Path(out).exists()


def test_to_xlsx_specific_sheets(tmp_path):
    ff = FlatFHIR(str(SAMPLE))
    out = str(tmp_path / "test.xlsx")
    ff.to_xlsx(out, sheets=["patient", "vitals"])
    assert Path(out).exists()


def test_to_csv(tmp_path):
    ff = FlatFHIR(str(SAMPLE))
    out = str(tmp_path / "test.csv")
    ff.to_csv(out, sheet="vitals")
    assert Path(out).exists()


def test_to_csv_empty_sheet(tmp_path):
    ff = FlatFHIR(str(SAMPLE))
    out = str(tmp_path / "empty.csv")
    ff.to_csv(out, sheet="imaging")
    assert Path(out).exists()


def test_to_parquet(tmp_path):
    pytest.importorskip("pyarrow")
    ff = FlatFHIR(str(SAMPLE))
    out = str(tmp_path / "test.parquet")
    ff.to_parquet(out)
    assert Path(out).exists()


def test_to_json(tmp_path):
    ff = FlatFHIR(str(SAMPLE))
    out = str(tmp_path / "test.json")
    ff.to_json(out)
    with open(out) as f:
        data = json.load(f)
    assert "patient" in data


def test_to_fhir():
    ff = FlatFHIR(str(SAMPLE))
    bundle = ff.to_fhir()
    assert bundle["resourceType"] == "Bundle"
    assert bundle["type"] == "collection"
    assert len(bundle["entry"]) > 0


def test_to_prompt():
    ff = FlatFHIR(str(SAMPLE))
    prompt = ff.to_prompt()
    assert "Jane Doe" in prompt


def test_to_prompt_max_tokens():
    ff = FlatFHIR(str(SAMPLE))
    prompt = ff.to_prompt(max_tokens=10)
    assert len(prompt) <= 100  # 10 * 4 + buffer


def test_to_prompt_empty_bundle():
    bundle = {"resourceType": "Bundle", "entry": []}
    ff = FlatFHIR(bundle)
    prompt = ff.to_prompt()
    assert isinstance(prompt, str)


# ── _make_primary_key ──

def test_primary_key_deterministic():
    row = pd.Series({"source_resource_id": "obs-1", "performer": "Dr. Smith", "timestamp": "2024-01-01"})
    pk1 = _make_primary_key("vitals", row)
    pk2 = _make_primary_key("vitals", row)
    assert pk1 == pk2
    assert len(pk1) == 12


def test_primary_key_different_sheets():
    row = pd.Series({"source_resource_id": "obs-1", "performer": "", "timestamp": ""})
    pk_vitals = _make_primary_key("vitals", row)
    pk_labs = _make_primary_key("labs", row)
    assert pk_vitals != pk_labs


# ── _pivot_observations ──

def test_pivot_empty():
    df = pd.DataFrame()
    result = _pivot_observations(df)
    assert result.empty


def test_pivot_groups_by_timestamp():
    df = pd.DataFrame([
        {"timestamp": "2024-01-01", "primary_key": "1", "heart_rate": 72},
        {"timestamp": "2024-01-01", "primary_key": "2", "bp_systolic": 120},
        {"timestamp": "2024-01-02", "primary_key": "3", "heart_rate": 75},
    ])
    result = _pivot_observations(df)
    assert len(result) == 2  # Two unique timestamps


def test_pivot_no_value_cols():
    df = pd.DataFrame([
        {"timestamp": "2024-01-01", "primary_key": "1",
         "encounter_id": "", "performer": "", "source_resource_id": "", "notes": ""},
    ])
    result = _pivot_observations(df)
    assert result.equals(df)
