"""Convert flat XLSX sheets back to a FHIR Bundle JSON (best-effort round-trip)."""

from __future__ import annotations

import json
import uuid
from pathlib import Path
from typing import Any

import pandas as pd

from flat_fhir.registry import _registry, LOINC, SNOMED, ICD10, RXNORM, CVX


def unflatten_xlsx(path: str) -> dict:
    """Read an XLSX file and reconstruct a FHIR Bundle."""
    xlsx = pd.ExcelFile(path)
    sheets = {}
    for name in xlsx.sheet_names:
        sheets[name] = xlsx.parse(name)
    return unflatten_sheets(sheets)


def unflatten_sheets(sheets: dict[str, pd.DataFrame]) -> dict:
    """Reconstruct a FHIR Bundle from DataFrames."""
    entries = []

    # Patient
    if "patient" in sheets and not sheets["patient"].empty:
        for _, row in sheets["patient"].iterrows():
            entries.append({"resource": _build_patient(row)})

    # Observations (vitals + labs)
    for sheet_name in ["vitals", "labs"]:
        if sheet_name in sheets and not sheets[sheet_name].empty:
            for _, row in sheets[sheet_name].iterrows():
                resource = _build_observation(row, sheet_name)
                if resource:
                    entries.append({"resource": resource})

    # Medications
    if "medications" in sheets and not sheets["medications"].empty:
        for _, row in sheets["medications"].iterrows():
            entries.append({"resource": _build_medication_request(row)})

    # Conditions
    if "conditions" in sheets and not sheets["conditions"].empty:
        for _, row in sheets["conditions"].iterrows():
            entries.append({"resource": _build_condition(row)})

    # Allergies
    if "allergies" in sheets and not sheets["allergies"].empty:
        for _, row in sheets["allergies"].iterrows():
            entries.append({"resource": _build_allergy(row)})

    # Procedures
    if "procedures" in sheets and not sheets["procedures"].empty:
        for _, row in sheets["procedures"].iterrows():
            entries.append({"resource": _build_procedure(row)})

    # Immunizations
    if "immunizations" in sheets and not sheets["immunizations"].empty:
        for _, row in sheets["immunizations"].iterrows():
            entries.append({"resource": _build_immunization(row)})

    # Encounters
    if "encounters" in sheets and not sheets["encounters"].empty:
        for _, row in sheets["encounters"].iterrows():
            entries.append({"resource": _build_encounter(row)})

    return {
        "resourceType": "Bundle",
        "type": "collection",
        "entry": entries,
    }


def _val(row: pd.Series, key: str, default: Any = "") -> Any:
    """Get a value from a row, returning default if missing or NaN."""
    v = row.get(key, default)
    if pd.isna(v):
        return default
    return v


def _build_patient(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "Patient",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
        "name": [{
            "family": _val(row, "family_name"),
            "given": [g for g in _val(row, "given_name").split() if g],
        }],
        "birthDate": _val(row, "birth_date"),
        "gender": _val(row, "gender"),
    }

    telecoms = []
    if _val(row, "phone"):
        telecoms.append({"system": "phone", "value": _val(row, "phone")})
    if _val(row, "email"):
        telecoms.append({"system": "email", "value": _val(row, "email")})
    if telecoms:
        resource["telecom"] = telecoms

    if _val(row, "address"):
        resource["address"] = [{"text": _val(row, "address")}]

    return resource


def _build_observation(row: pd.Series, sheet: str) -> dict | None:
    """Build an Observation resource from a flat row."""
    common = ["primary_key", "timestamp", "encounter_id", "performer", "source_resource_id", "notes"]
    value_cols = [c for c in row.index if c not in common and pd.notna(row[c])]

    if not value_cols:
        return None

    # Take the first value column
    col = value_cols[0]
    value = row[col]

    # Look up the code mapping
    mapping = _registry.lookup_column(col)

    category_code = "vital-signs" if sheet == "vitals" else "laboratory"

    resource: dict[str, Any] = {
        "resourceType": "Observation",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
        "status": "final",
        "category": [{
            "coding": [{
                "system": "http://terminology.hl7.org/CodeSystem/observation-category",
                "code": category_code,
            }]
        }],
        "effectiveDateTime": _val(row, "timestamp"),
    }

    if mapping:
        resource["code"] = {
            "coding": [{"system": mapping.system, "code": mapping.code, "display": mapping.display}],
            "text": mapping.display,
        }
        if mapping.unit and isinstance(value, (int, float)):
            resource["valueQuantity"] = {
                "value": value,
                "unit": mapping.unit,
                "system": "http://unitsofmeasure.org",
            }
        elif isinstance(value, (int, float)):
            resource["valueQuantity"] = {"value": value}
        else:
            resource["valueString"] = str(value)
    else:
        resource["code"] = {"text": col}
        if isinstance(value, (int, float)):
            resource["valueQuantity"] = {"value": value}
        else:
            resource["valueString"] = str(value)

    if _val(row, "encounter_id"):
        resource["encounter"] = {"reference": f"Encounter/{_val(row, 'encounter_id')}"}

    if _val(row, "performer"):
        resource["performer"] = [{"display": _val(row, "performer")}]

    return resource


def _build_medication_request(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "MedicationRequest",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
        "status": _val(row, "status") or "active",
        "intent": "order",
        "authoredOn": _val(row, "timestamp"),
    }

    med_name = _val(row, "medication_name")
    codings = []
    if _val(row, "rxnorm_code"):
        codings.append({"system": RXNORM, "code": _val(row, "rxnorm_code"), "display": med_name})
    if codings:
        resource["medicationCodeableConcept"] = {"coding": codings, "text": med_name}
    elif med_name:
        resource["medicationCodeableConcept"] = {"text": med_name}

    dosage: dict[str, Any] = {}
    if _val(row, "frequency"):
        dosage["text"] = _val(row, "frequency")
    if _val(row, "dose"):
        dosage["doseAndRate"] = [{"doseQuantity": {
            "value": _val(row, "dose"),
            "unit": _val(row, "dose_unit"),
        }}]
    if dosage:
        resource["dosageInstruction"] = [dosage]

    if _val(row, "prescriber"):
        resource["requester"] = {"display": _val(row, "prescriber")}

    if _val(row, "encounter_id"):
        resource["encounter"] = {"reference": f"Encounter/{_val(row, 'encounter_id')}"}

    return resource


def _build_condition(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "Condition",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
    }

    codings = []
    if _val(row, "icd_code"):
        codings.append({"system": ICD10, "code": _val(row, "icd_code"),
                        "display": _val(row, "condition_name")})
    if _val(row, "snomed_code"):
        codings.append({"system": SNOMED, "code": _val(row, "snomed_code"),
                        "display": _val(row, "condition_name")})
    if codings:
        resource["code"] = {"coding": codings, "text": _val(row, "condition_name")}
    elif _val(row, "condition_name"):
        resource["code"] = {"text": _val(row, "condition_name")}

    if _val(row, "clinical_status"):
        resource["clinicalStatus"] = {
            "coding": [{
                "system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
                "code": _val(row, "clinical_status").lower(),
            }]
        }

    if _val(row, "onset_date"):
        resource["onsetDateTime"] = _val(row, "onset_date")
    if _val(row, "abatement_date"):
        resource["abatementDateTime"] = _val(row, "abatement_date")
    if _val(row, "timestamp"):
        resource["recordedDate"] = _val(row, "timestamp")

    return resource


def _build_allergy(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "AllergyIntolerance",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
    }

    if _val(row, "substance"):
        resource["code"] = {"text": _val(row, "substance")}

    if _val(row, "criticality"):
        resource["criticality"] = _val(row, "criticality")

    if _val(row, "reaction") or _val(row, "severity"):
        reaction: dict[str, Any] = {}
        if _val(row, "reaction"):
            reaction["manifestation"] = [{"text": _val(row, "reaction")}]
        if _val(row, "severity"):
            reaction["severity"] = _val(row, "severity")
        resource["reaction"] = [reaction]

    if _val(row, "timestamp"):
        resource["recordedDate"] = _val(row, "timestamp")

    return resource


def _build_procedure(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "Procedure",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
        "status": _val(row, "status") or "completed",
    }

    codings = []
    if _val(row, "snomed_code"):
        codings.append({"system": SNOMED, "code": _val(row, "snomed_code"),
                        "display": _val(row, "procedure_name")})
    if codings:
        resource["code"] = {"coding": codings, "text": _val(row, "procedure_name")}
    elif _val(row, "procedure_name"):
        resource["code"] = {"text": _val(row, "procedure_name")}

    if _val(row, "timestamp"):
        resource["performedDateTime"] = _val(row, "timestamp")

    if _val(row, "body_site"):
        resource["bodySite"] = [{"text": _val(row, "body_site")}]

    if _val(row, "outcome"):
        resource["outcome"] = {"text": _val(row, "outcome")}

    return resource


def _build_immunization(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "Immunization",
        "id": _val(row, "source_resource_id") or str(uuid.uuid4()),
        "status": _val(row, "status") or "completed",
    }

    codings = []
    if _val(row, "cvx_code"):
        codings.append({"system": CVX, "code": _val(row, "cvx_code"),
                        "display": _val(row, "vaccine_name")})
    if codings:
        resource["vaccineCode"] = {"coding": codings, "text": _val(row, "vaccine_name")}
    elif _val(row, "vaccine_name"):
        resource["vaccineCode"] = {"text": _val(row, "vaccine_name")}

    if _val(row, "timestamp"):
        resource["occurrenceDateTime"] = _val(row, "timestamp")

    if _val(row, "lot_number"):
        resource["lotNumber"] = _val(row, "lot_number")

    if _val(row, "site"):
        resource["site"] = {"text": _val(row, "site")}

    if _val(row, "dose") or _val(row, "dose_unit"):
        resource["doseQuantity"] = {
            "value": _val(row, "dose"),
            "unit": _val(row, "dose_unit"),
        }

    return resource


def _build_encounter(row: pd.Series) -> dict:
    resource: dict[str, Any] = {
        "resourceType": "Encounter",
        "id": _val(row, "source_resource_id") or _val(row, "encounter_id") or str(uuid.uuid4()),
        "status": _val(row, "status") or "finished",
    }

    if _val(row, "type"):
        resource["type"] = [{"text": _val(row, "type")}]

    period: dict[str, str] = {}
    if _val(row, "period_start") or _val(row, "timestamp"):
        period["start"] = _val(row, "period_start") or _val(row, "timestamp")
    if _val(row, "period_end"):
        period["end"] = _val(row, "period_end")
    if period:
        resource["period"] = period

    if _val(row, "facility"):
        resource["serviceProvider"] = {"display": _val(row, "facility")}

    if _val(row, "performer"):
        resource["participant"] = [{"individual": {"display": _val(row, "performer")}}]

    if _val(row, "reason"):
        reasons = [r.strip() for r in str(_val(row, "reason")).split(";") if r.strip()]
        resource["reasonCode"] = [{"text": r} for r in reasons]

    if _val(row, "discharge_disposition"):
        resource["hospitalization"] = {
            "dischargeDisposition": {"text": _val(row, "discharge_disposition")}
        }

    return resource
