#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Comprehensive test suite for FHIR to PADneXt converter.

Run with:
    pytest test_fhir_to_pad_converter.py -v
    pytest test_fhir_to_pad_converter.py -v --cov=. --cov-report=html
"""

import pytest
import json
import xml.etree.ElementTree as ET
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, List
from typer.testing import CliRunner

# Import modules to test
from utils import (
    parse_iso_date,
    format_iso_date,
    get_ref_id,
    ensure_text,
    collect_effective_dates,
    validate_file_path,
    validate_output_path,
    validate_directory_path
)
from validation import (
    validate_temporal_consistency,
    validate_codes,
    run_validation
)
from translator import CodeTranslator
from fhir_to_pad_converter import (
    validate_fhir_json,
    compute_fhir_stats,
    group_entries,
    claim_item_to_position,
    find_resource_by_ref,
    claim_to_rechnung_header,
    validate_ziffer,
    get_with_placeholder,
    validate_pad_xml,
    compute_pad_stats,
    build_pad_xml,
    PAD_NS,
    app
)


# ============================================================================
# FIXTURES
# ============================================================================

@pytest.fixture
def sample_fhir_bundle():
    """Minimal valid FHIR bundle for testing."""
    return {
        "resourceType": "Bundle",
        "type": "collection",
        "entry": [
            {
                "resource": {
                    "resourceType": "Patient",
                    "id": "patient-1",
                    "name": [{"family": "Test", "given": ["John"]}],
                    "birthDate": "1980-01-01",
                    "gender": "male"
                }
            },
            {
                "resource": {
                    "resourceType": "Encounter",
                    "id": "encounter-1",
                    "status": "finished",
                    "subject": {"reference": "Patient/patient-1"},
                    "period": {
                        "start": "2024-01-01T10:00:00+00:00",
                        "end": "2024-01-01T12:00:00+00:00"
                    }
                }
            },
            {
                "resource": {
                    "resourceType": "Observation",
                    "id": "obs-1",
                    "status": "final",
                    "code": {
                        "coding": [{
                            "system": "http://loinc.org",
                            "code": "12345-6",
                            "display": "Test Observation"
                        }]
                    },
                    "subject": {"reference": "Patient/patient-1"},
                    "encounter": {"reference": "Encounter/encounter-1"},
                    "effectiveDateTime": "2024-01-01T10:30:00+00:00"
                }
            }
        ]
    }


@pytest.fixture
def sample_claim_bundle():
    """FHIR bundle with Claim resource."""
    return {
        "resourceType": "Bundle",
        "type": "collection",
        "entry": [
            {
                "resource": {
                    "resourceType": "Patient",
                    "id": "patient-1",
                    "name": [{"family": "Smith", "given": ["Jane"]}],
                    "birthDate": "1990-05-15",
                    "gender": "female"
                }
            },
            {
                "resource": {
                    "resourceType": "Organization",
                    "id": "provider-org-1",
                    "name": "Test Hospital"
                }
            },
            {
                "resource": {
                    "resourceType": "Organization",
                    "id": "insurer-org-1",
                    "name": "Test Insurance"
                }
            },
            {
                "resource": {
                    "resourceType": "Claim",
                    "id": "claim-1",
                    "status": "active",
                    "patient": {"reference": "Patient/patient-1"},
                    "provider": {"reference": "Organization/provider-org-1"},
                    "insurer": {"reference": "Organization/insurer-org-1"},
                    "created": "2024-01-15T10:00:00+00:00",
                    "diagnosis": [{
                        "diagnosisCodeableConcept": {
                            "coding": [{
                                "system": "http://hl7.org/fhir/sid/icd-10",
                                "code": "Z00.0",
                                "display": "General examination"
                            }]
                        }
                    }],
                    "item": [
                        {
                            "sequence": 1,
                            "productOrService": {
                                "coding": [{
                                    "system": "http://test.de/goa",
                                    "code": "1",
                                    "display": "Consultation"
                                }]
                            },
                            "servicedDate": "2024-01-15"
                        }
                    ]
                }
            }
        ]
    }


@pytest.fixture
def sample_header_config():
    """Sample header configuration."""
    return {
        "nachrichtentyp_version": "1.0",
        "rechnungsersteller_name": "Test Clinic",
        "rechnungsersteller_kundennr": "12345",
        "rechnungsersteller_strasse": "Main St 1",
        "rechnungsersteller_plz": "12345",
        "rechnungsersteller_ort": "Berlin",
        "leistungserbringer_id": "LE001",
        "leistungserbringer_vorname": "Dr. Max",
        "leistungserbringer_name": "Mustermann",
        "behandelter_vorname": "John",
        "behandelter_name": "Doe",
        "behandelter_gebdatum": "1980-01-01",
        "behandelter_geschlecht": "m",
        "versicherter_vorname": "John",
        "versicherter_name": "Doe",
        "versicherter_gebdatum": "1980-01-01",
        "versicherter_geschlecht": "m",
        "behandlungsart": "0",
        "vertragsart": "1",
        "diagnose_text": "Test diagnosis",
        "diagnose_datum": "2024-01-01"
    }


@pytest.fixture
def sample_placeholder_config():
    """Sample placeholder configuration."""
    return {
        "rechnungsersteller": {
            "name": "PLACEHOLDER_CLINIC",
            "plz": "00000",
            "ort": "PLACEHOLDER_CITY",
            "strasse": "PLACEHOLDER_STREET"
        },
        "leistungserbringer": {
            "vorname": "PLACEHOLDER_FIRSTNAME",
            "name": "PLACEHOLDER_LASTNAME"
        },
        "behandelter": {
            "anrede": "Ohne Anrede",
            "vorname": "PLACEHOLDER_PATIENT",
            "name": "PLACEHOLDER_PATIENT",
            "gebdatum": "1900-01-01",
            "geschlecht": "u"
        },
        "goziffer": {
            "ziffer": "99999",
            "datum": "1900-01-01"
        }
    }


# ============================================================================
# UTILS.PY TESTS
# ============================================================================

class TestUtils:
    """Tests for utility functions."""

    def test_parse_iso_date_valid_with_z(self):
        """Test parsing ISO date with Z suffix."""
        result = parse_iso_date("2024-01-01T10:00:00Z")
        assert result is not None
        assert result.year == 2024
        assert result.month == 1
        assert result.day == 1

    def test_parse_iso_date_valid_with_timezone(self):
        """Test parsing ISO date with timezone."""
        result = parse_iso_date("2024-01-01T10:00:00+02:00")
        assert result is not None
        assert result.year == 2024

    def test_parse_iso_date_invalid(self):
        """Test parsing invalid date returns None."""
        assert parse_iso_date("not-a-date") is None
        assert parse_iso_date("") is None
        assert parse_iso_date(None) is None

    def test_parse_iso_date_edge_cases(self):
        """Test edge cases for date parsing."""
        # Just date without time
        result = parse_iso_date("2024-01-01")
        assert result is not None
        assert result.year == 2024

    def test_format_iso_date(self):
        """Test formatting datetime to ISO date string."""
        dt = datetime(2024, 1, 15, 10, 30, 45)
        result = format_iso_date(dt)
        assert result == "2024-01-15"

    def test_get_ref_id_valid(self):
        """Test extracting ID from valid reference."""
        assert get_ref_id("Patient/123") == "123"
        assert get_ref_id("Encounter/abc-def") == "abc-def"
        assert get_ref_id("Organization/org-1") == "org-1"

    def test_get_ref_id_invalid(self):
        """Test handling invalid references."""
        assert get_ref_id("NoSlash") is None
        assert get_ref_id("") is None
        assert get_ref_id(None) is None
        assert get_ref_id("Patient/") is None

    def test_ensure_text_with_element(self):
        """Test extracting text from XML element."""
        elem = ET.Element("test")
        elem.text = "  content  "
        assert ensure_text(elem) == "content"

    def test_ensure_text_none(self):
        """Test ensure_text with None returns default."""
        assert ensure_text(None) == ""
        assert ensure_text(None, "default") == "default"

    def test_collect_effective_dates(self):
        """Test collecting dates from resource."""
        resource = {
            "effectiveDateTime": "2024-01-01T10:00:00Z",
            "issued": "2024-01-02T10:00:00Z",
            "meta": {
                "lastUpdated": "2024-01-03T10:00:00Z"
            }
        }
        dates = collect_effective_dates(resource)
        assert len(dates) == 3
        assert all(isinstance(d, datetime) for d in dates)

    def test_collect_effective_dates_empty(self):
        """Test collecting dates from resource with no dates."""
        resource = {"resourceType": "Patient"}
        dates = collect_effective_dates(resource)
        assert len(dates) == 0


# ============================================================================
# VALIDATION.PY TESTS
# ============================================================================

class TestValidation:
    """Tests for validation functions."""

    def test_validate_temporal_consistency_valid(self):
        """Test validation passes for valid dates."""
        resources = [{
            "id": "test-1",
            "effectiveDateTime": "2024-01-01T10:00:00Z"
        }]
        warnings = validate_temporal_consistency(resources)
        assert len(warnings) == 0

    def test_validate_temporal_consistency_future_date(self):
        """Test validation warns for future dates."""
        resources = [{
            "id": "test-1",
            "effectiveDateTime": "2099-01-01T10:00:00Z"
        }]
        warnings = validate_temporal_consistency(resources)
        assert len(warnings) > 0
        assert "future" in warnings[0].lower()

    def test_validate_codes(self):
        """Test code validation (currently stub)."""
        resources = [{"id": "test-1"}]
        warnings = validate_codes(resources)
        assert isinstance(warnings, list)

    def test_run_validation(self):
        """Test running all validations."""
        resources = [{
            "id": "test-1",
            "effectiveDateTime": "2024-01-01T10:00:00Z"
        }]
        warnings = run_validation(resources)
        assert isinstance(warnings, list)


# ============================================================================
# TRANSLATOR.PY TESTS
# ============================================================================

class TestCodeTranslator:
    """Tests for code translation."""

    def test_translator_init(self):
        """Test translator initialization."""
        translator = CodeTranslator()
        assert translator.maps == {}

    def test_translator_parse_concept_map(self):
        """Test parsing a concept map."""
        translator = CodeTranslator()
        concept_map = {
            "group": [{
                "source": "http://loinc.org",
                "target": "http://test.de/goa",
                "element": [
                    {
                        "code": "12345-6",
                        "target": [{"code": "001"}]
                    }
                ]
            }]
        }
        translator._parse_concept_map(concept_map)
        assert "http://loinc.org" in translator.maps
        result = translator.translate("http://loinc.org", "12345-6")
        assert result == "001"

    def test_translator_no_match(self):
        """Test translation returns None when no match."""
        translator = CodeTranslator()
        result = translator.translate("http://unknown.org", "12345")
        assert result is None


# ============================================================================
# FHIR VALIDATION TESTS
# ============================================================================

class TestFhirValidation:
    """Tests for FHIR validation functions."""

    def test_validate_fhir_json_valid_bundle(self, sample_fhir_bundle):
        """Test validation of valid FHIR bundle."""
        ok, messages = validate_fhir_json(sample_fhir_bundle)
        assert ok is True
        assert len(messages) > 0

    def test_validate_fhir_json_invalid_resource_type(self):
        """Test validation fails for wrong resourceType."""
        bundle = {"resourceType": "NotABundle"}
        ok, messages = validate_fhir_json(bundle)
        assert ok is False
        assert any("Bundle" in m for m in messages)

    def test_validate_fhir_json_missing_type(self):
        """Test validation warns for missing bundle type."""
        bundle = {"resourceType": "Bundle", "entry": []}
        ok, messages = validate_fhir_json(bundle)
        assert ok is False

    def test_validate_fhir_json_invalid_entry(self):
        """Test validation handles invalid entry."""
        bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": "not-a-list"
        }
        ok, messages = validate_fhir_json(bundle)
        assert ok is False

    def test_compute_fhir_stats(self, sample_fhir_bundle):
        """Test computing FHIR statistics."""
        stats = compute_fhir_stats(sample_fhir_bundle)
        assert stats["bundle_type"] == "collection"
        assert stats["total_entries"] == 3
        assert "Patient" in stats["resource_type_counts"]
        assert stats["resource_type_counts"]["Patient"] == 1


# ============================================================================
# GROUPING TESTS
# ============================================================================

class TestGrouping:
    """Tests for resource grouping logic."""

    def test_group_entries_by_encounter(self, sample_fhir_bundle):
        """Test grouping by encounter when no Claims."""
        groups = group_entries(sample_fhir_bundle)
        assert len(groups) > 0
        # Should have one group for (patient-1, encounter-1)
        assert ("patient-1", "encounter-1") in groups

    def test_group_entries_by_claim(self, sample_claim_bundle):
        """Test grouping by claim when Claims present."""
        groups = group_entries(sample_claim_bundle)
        assert len(groups) > 0
        # Should have one group for (patient-1, claim-1)
        assert ("patient-1", "claim-1") in groups

    def test_group_entries_empty_bundle(self):
        """Test grouping empty bundle."""
        bundle = {"resourceType": "Bundle", "type": "collection", "entry": []}
        groups = group_entries(bundle)
        assert len(groups) == 0


# ============================================================================
# CLAIM MAPPING TESTS
# ============================================================================

class TestClaimMapping:
    """Tests for Claim-to-PAD mapping functions."""

    def test_claim_item_to_position(self):
        """Test converting Claim item to position."""
        item = {
            "sequence": 1,
            "productOrService": {
                "coding": [{
                    "code": "12345",
                    "display": "Test Service"
                }]
            },
            "servicedDate": "2024-01-15"
        }
        position = claim_item_to_position(item)
        assert position["id"] == 1
        assert position["ziffer"] == "12345"
        assert position["text"] == "Test Service"
        assert position["datum"] == "2024-01-15"
        assert position["anzahl"] == "1"

    def test_claim_item_to_position_missing_fields(self):
        """Test converting Claim item with missing fields."""
        item = {"sequence": 2}
        position = claim_item_to_position(item)
        assert position["id"] == 2
        assert position["ziffer"] == ""
        assert position["text"] == ""

    def test_find_resource_by_ref(self, sample_claim_bundle):
        """Test finding resource by reference."""
        resource = find_resource_by_ref(sample_claim_bundle, "Patient/patient-1")
        assert resource is not None
        assert resource["resourceType"] == "Patient"
        assert resource["id"] == "patient-1"

    def test_find_resource_by_ref_not_found(self, sample_claim_bundle):
        """Test finding non-existent resource."""
        resource = find_resource_by_ref(sample_claim_bundle, "Patient/does-not-exist")
        assert resource is None

    def test_find_resource_by_ref_invalid_format(self, sample_claim_bundle):
        """Test finding resource with invalid reference format."""
        resource = find_resource_by_ref(sample_claim_bundle, "InvalidReference")
        assert resource is None

    def test_claim_to_rechnung_header(self, sample_claim_bundle):
        """Test extracting header info from Claim."""
        claim = sample_claim_bundle["entry"][3]["resource"]
        header = claim_to_rechnung_header(claim, sample_claim_bundle)

        assert "behandelter_vorname" in header
        assert header["behandelter_name"] == "Smith"
        assert header["behandelter_gebdatum"] == "1990-05-15"
        assert header["leistungserbringer_name"] == "Test Hospital"
        assert header["empfaenger_name"] == "Test Insurance"


# ============================================================================
# PLACEHOLDER & VALIDATION TESTS
# ============================================================================

class TestPlaceholders:
    """Tests for placeholder and validation functions."""

    def test_validate_ziffer_valid(self):
        """Test validating valid ziffer code."""
        auto_filled = []
        result = validate_ziffer("12345", "99999", "test.ziffer", auto_filled)
        assert result == "12345"
        assert len(auto_filled) == 0

    def test_validate_ziffer_empty(self):
        """Test validating empty ziffer uses placeholder."""
        auto_filled = []
        result = validate_ziffer("", "99999", "test.ziffer", auto_filled)
        assert result == "99999"
        assert len(auto_filled) == 1
        assert "99999" in auto_filled[0]

    def test_validate_ziffer_too_long(self):
        """Test validating ziffer that's too long gets truncated."""
        auto_filled = []
        result = validate_ziffer("123456789", "99999", "test.ziffer", auto_filled)
        assert result == "12345678"
        assert len(result) == 8
        assert len(auto_filled) == 1
        assert "truncated" in auto_filled[0].lower()

    def test_get_with_placeholder_has_value(self):
        """Test get_with_placeholder with existing value."""
        auto_filled = []
        result = get_with_placeholder("RealValue", "Placeholder", "test.field", auto_filled)
        assert result == "RealValue"
        assert len(auto_filled) == 0

    def test_get_with_placeholder_uses_placeholder(self):
        """Test get_with_placeholder uses placeholder for empty value."""
        auto_filled = []
        result = get_with_placeholder("", "Placeholder", "test.field", auto_filled)
        assert result == "Placeholder"
        assert len(auto_filled) == 1
        assert "Placeholder" in auto_filled[0]


# ============================================================================
# XML BUILDING TESTS
# ============================================================================

class TestXmlBuilding:
    """Tests for PAD XML building functions."""

    def test_build_pad_xml_basic(self, sample_fhir_bundle, sample_header_config, sample_placeholder_config):
        """Test building basic PAD XML."""
        root, warnings, header, auto_filled = build_pad_xml(
            sample_fhir_bundle,
            header_cfg=sample_header_config,
            placeholder_cfg=sample_placeholder_config
        )

        assert root is not None
        assert root.tag == f"{{{PAD_NS}}}rechnungen"
        assert root.get("anzahl") is not None

    def test_build_pad_xml_with_claim(self, sample_claim_bundle, sample_header_config, sample_placeholder_config):
        """Test building PAD XML with Claim resource."""
        root, warnings, header, auto_filled = build_pad_xml(
            sample_claim_bundle,
            header_cfg=sample_header_config,
            placeholder_cfg=sample_placeholder_config
        )

        assert root is not None
        # Check that claim data overrode config
        assert header["behandelter_name"] == "Smith"
        assert header["leistungserbringer_name"] == "Test Hospital"

    def test_build_pad_xml_namespace(self, sample_fhir_bundle, sample_header_config):
        """Test PAD XML has correct namespace."""
        root, _, _, _ = build_pad_xml(sample_fhir_bundle, header_cfg=sample_header_config)
        assert root.get("xmlns") == PAD_NS


# ============================================================================
# PAD VALIDATION TESTS
# ============================================================================

class TestPadValidation:
    """Tests for PAD XML validation functions."""

    def test_validate_pad_xml_well_formed(self, sample_fhir_bundle, sample_header_config):
        """Test validation of well-formed XML."""
        root, _, _, _ = build_pad_xml(sample_fhir_bundle, header_cfg=sample_header_config)
        ok, messages = validate_pad_xml(root)
        assert ok is True
        assert any("well-formed" in m for m in messages)

    def test_compute_pad_stats(self, sample_fhir_bundle, sample_header_config):
        """Test computing PAD statistics."""
        root, _, _, _ = build_pad_xml(sample_fhir_bundle, header_cfg=sample_header_config)
        stats = compute_pad_stats(root)

        assert "rechnungen_declared" in stats
        assert "rechnungen_actual" in stats
        assert "goziffer_count" in stats
        assert isinstance(stats["warnings"], list)


# ============================================================================
# INTEGRATION TESTS
# ============================================================================

class TestIntegration:
    """End-to-end integration tests."""

    def test_full_conversion_encounter_based(self, sample_fhir_bundle, sample_header_config, sample_placeholder_config):
        """Test complete conversion workflow (encounter-based)."""
        # Validate input
        fhir_ok, _ = validate_fhir_json(sample_fhir_bundle)
        assert fhir_ok

        # Compute stats
        stats = compute_fhir_stats(sample_fhir_bundle)
        assert stats["total_entries"] == 3

        # Build XML
        root, warnings, header, auto_filled = build_pad_xml(
            sample_fhir_bundle,
            header_cfg=sample_header_config,
            placeholder_cfg=sample_placeholder_config
        )
        assert root is not None

        # Validate output
        pad_ok, _ = validate_pad_xml(root)
        assert pad_ok

        # Compute output stats
        pad_stats = compute_pad_stats(root)
        assert pad_stats["goziffer_count"] > 0

    def test_full_conversion_claim_based(self, sample_claim_bundle, sample_header_config, sample_placeholder_config):
        """Test complete conversion workflow (claim-based)."""
        # Validate input
        fhir_ok, _ = validate_fhir_json(sample_claim_bundle)
        assert fhir_ok

        # Build XML
        root, warnings, header, auto_filled = build_pad_xml(
            sample_claim_bundle,
            header_cfg=sample_header_config,
            placeholder_cfg=sample_placeholder_config
        )
        assert root is not None

        # Check claim data was used
        assert header["behandelter_name"] == "Smith"

        # Validate output
        pad_ok, _ = validate_pad_xml(root)
        assert pad_ok

    def test_conversion_with_missing_data(self, sample_placeholder_config):
        """Test conversion with incomplete FHIR data uses placeholders."""
        # Minimal bundle with missing fields
        minimal_bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": [{
                "resource": {
                    "resourceType": "Patient",
                    "id": "p1"
                    # Missing name, birthDate, etc.
                }
            }]
        }

        root, warnings, header, auto_filled = build_pad_xml(
            minimal_bundle,
            header_cfg={},
            placeholder_cfg=sample_placeholder_config
        )

        # Should have many auto-filled fields
        assert len(auto_filled) > 0
        # XML should still be valid
        assert root is not None


# ============================================================================
# EDGE CASE TESTS
# ============================================================================

class TestEdgeCases:
    """Tests for edge cases and error conditions."""

    def test_empty_bundle(self):
        """Test handling empty bundle."""
        bundle = {"resourceType": "Bundle", "type": "collection", "entry": []}
        ok, messages = validate_fhir_json(bundle)
        assert ok is True

    def test_bundle_with_null_entries(self):
        """Test handling bundle with None entries."""
        bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": [None, {"resource": {"resourceType": "Patient", "id": "p1"}}]
        }
        groups = group_entries(bundle)
        # Should handle None gracefully
        assert isinstance(groups, dict)

    def test_resource_without_subject(self):
        """Test handling resource without subject reference."""
        bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": [{
                "resource": {
                    "resourceType": "Observation",
                    "id": "obs-1",
                    "status": "final"
                    # No subject field
                }
            }]
        }
        stats = compute_fhir_stats(bundle)
        assert stats["entries_missing_subject"] == 1

    def test_claim_with_empty_items(self):
        """Test handling Claim with empty items array."""
        item = {"sequence": 1}  # Missing productOrService
        position = claim_item_to_position(item)
        assert position["ziffer"] == ""
        assert position["text"] == ""

    def test_reference_with_multiple_slashes(self):
        """Test handling malformed reference with multiple slashes."""
        # Current implementation will only split on first slash
        ref_id = get_ref_id("http://example.com/fhir/Patient/123")
        # Should return the last part or None
        assert ref_id is not None or ref_id is None  # Either is acceptable

    def test_date_parsing_various_formats(self):
        """Test parsing various date formats."""
        dates = [
            "2024-01-01",
            "2024-01-01T10:00:00",
            "2024-01-01T10:00:00Z",
            "2024-01-01T10:00:00+02:00",
            "2024-01-01T10:00:00.123456Z"
        ]
        for date_str in dates:
            result = parse_iso_date(date_str)
            assert result is not None or result is None  # Should not crash


# ============================================================================
# PERFORMANCE TESTS (Optional)
# ============================================================================

class TestPerformance:
    """Basic performance tests."""

    def test_large_bundle_grouping(self):
        """Test grouping with larger bundle."""
        # Create bundle with 100 observations
        entries = []
        for i in range(100):
            entries.append({
                "resource": {
                    "resourceType": "Observation",
                    "id": f"obs-{i}",
                    "status": "final",
                    "subject": {"reference": f"Patient/patient-{i % 10}"},
                    "encounter": {"reference": f"Encounter/enc-{i % 10}"},
                    "effectiveDateTime": "2024-01-01T10:00:00Z"
                }
            })

        bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": entries
        }

        # Should complete quickly
        import time
        start = time.time()
        groups = group_entries(bundle)
        elapsed = time.time() - start

        assert elapsed < 1.0  # Should take less than 1 second
        assert len(groups) == 10  # 10 unique (patient, encounter) combinations


# ============================================================================
# INPUT VALIDATION TESTS
# ============================================================================

class TestInputValidation:
    """Tests for input path validation."""

    def test_validate_file_path_existing_file(self, tmp_path):
        """Test validation of existing file."""
        # Create a temporary file
        test_file = tmp_path / "test.json"
        test_file.write_text('{"test": true}')

        # Should succeed
        result = validate_file_path(str(test_file), must_exist=True)
        assert Path(result).exists()
        assert Path(result).is_absolute()

    def test_validate_file_path_nonexistent_file(self, tmp_path):
        """Test validation fails for nonexistent file."""
        test_file = tmp_path / "nonexistent.json"

        # Should raise FileNotFoundError
        with pytest.raises(FileNotFoundError):
            validate_file_path(str(test_file), must_exist=True)

    def test_validate_file_path_empty_path(self):
        """Test validation fails for empty path."""
        with pytest.raises(ValueError, match="File path cannot be empty"):
            validate_file_path("")

    def test_validate_file_path_none_path(self):
        """Test validation fails for None path."""
        with pytest.raises(ValueError, match="File path must be a string"):
            validate_file_path(None)

    def test_validate_output_path_creates_directory(self, tmp_path):
        """Test output path validation creates parent directory."""
        output_file = tmp_path / "new_dir" / "output.xml"

        # Should succeed and create parent directory
        result = validate_output_path(str(output_file))
        assert Path(result).parent.exists()
        assert Path(result).parent.is_dir()

    def test_validate_output_path_existing_file_overwrite(self, tmp_path):
        """Test output path with existing file (overwrite=True)."""
        output_file = tmp_path / "existing.xml"
        output_file.write_text('<test/>')

        # Should succeed (overwrite allowed by default)
        result = validate_output_path(str(output_file), overwrite=True)
        assert Path(result).exists()

    def test_validate_output_path_existing_file_no_overwrite(self, tmp_path):
        """Test output path fails with existing file when overwrite=False."""
        output_file = tmp_path / "existing.xml"
        output_file.write_text('<test/>')

        # Should raise FileExistsError
        with pytest.raises(FileExistsError):
            validate_output_path(str(output_file), overwrite=False)

    def test_validate_directory_path_existing(self, tmp_path):
        """Test directory validation for existing directory."""
        result = validate_directory_path(str(tmp_path), must_exist=True)
        assert Path(result).is_dir()
        assert Path(result).is_absolute()

    def test_validate_directory_path_create(self, tmp_path):
        """Test directory validation creates directory."""
        new_dir = tmp_path / "new_directory"

        result = validate_directory_path(str(new_dir), must_exist=False, create=True)
        assert Path(result).exists()
        assert Path(result).is_dir()

    def test_validate_directory_path_nonexistent(self, tmp_path):
        """Test directory validation fails for nonexistent directory."""
        new_dir = tmp_path / "nonexistent_dir"

        with pytest.raises(FileNotFoundError):
            validate_directory_path(str(new_dir), must_exist=True, create=False)

    def test_validate_directory_path_file_not_directory(self, tmp_path):
        """Test directory validation fails when path is a file."""
        test_file = tmp_path / "file.txt"
        test_file.write_text("content")

        with pytest.raises(NotADirectoryError):
            validate_directory_path(str(test_file), must_exist=True)


# ============================================================================
# CONFIG VALIDATION TESTS
# ============================================================================

class TestConfigValidation:
    """Tests for configuration validation."""

    def test_validate_placeholder_config(self):
        """Test placeholder config validation."""
        try:
            from config_schemas import validate_placeholder_config

            # Valid config
            valid_config = {
                "rechnungsersteller": {
                    "name": "Test",
                    "plz": "12345",
                    "ort": "Berlin",
                    "strasse": "Main St"
                },
                "leistungserbringer": {
                    "vorname": "Dr. John",
                    "name": "Doe"
                },
                "goziffer": {
                    "go": "EBM",
                    "ziffer": "12345",
                    "datum": "2024-01-01"
                }
            }

            warnings = validate_placeholder_config(valid_config)
            assert isinstance(warnings, list)

        except ImportError:
            pytest.skip("config_schemas module not available")

    def test_validate_invalid_placeholder_config(self):
        """Test placeholder config validation with invalid data."""
        try:
            from config_schemas import validate_placeholder_config

            # Invalid config - missing required fields
            invalid_config = {
                "rechnungsersteller": {
                    "name": "Test"
                    # Missing required fields
                }
            }

            with pytest.raises(ValueError):
                validate_placeholder_config(invalid_config)

        except ImportError:
            pytest.skip("config_schemas module not available")

    def test_validate_mapping_config(self):
        """Test mapping config validation."""
        try:
            from config_schemas import validate_mapping_config

            # Valid mapping config
            valid_config = {
                "resources": {
                    "Observation": {
                        "target": "goziffer",
                        "fields": {
                            "ziffer": {
                                "source": "code.coding[0].code"
                            }
                        }
                    }
                }
            }

            warnings = validate_mapping_config(valid_config)
            assert isinstance(warnings, list)

        except ImportError:
            pytest.skip("config_schemas module not available")


# ============================================================================
# CLI TESTS (Typer)
# ============================================================================

class TestCLI:
    """Tests for command-line interface using typer."""

    def setup_method(self):
        """Set up CLI test runner."""
        self.runner = CliRunner()

    def test_cli_help(self):
        """Test --help output."""
        result = self.runner.invoke(app, ["--help"])
        assert result.exit_code == 0
        assert "Convert FHIR Bundle JSON to PADneXt 2.12 XML format" in result.stdout
        assert "--input-json" in result.stdout
        assert "--output-dir" in result.stdout
        assert "--verbose" in result.stdout

    def test_cli_missing_required_arg(self):
        """Test error when required argument missing."""
        result = self.runner.invoke(app, [])
        assert result.exit_code != 0
        assert "Missing option '--input-json'" in result.stdout

    def test_cli_short_aliases_in_help(self):
        """Test that short aliases are shown in help."""
        result = self.runner.invoke(app, ["--help"])
        assert result.exit_code == 0
        assert "-i" in result.stdout  # Short alias for --input-json
        assert "-o" in result.stdout  # Short alias for --output-dir
        assert "-v" in result.stdout  # Short alias for --verbose
        assert "-m" in result.stdout  # Short alias for --mapping-config

    def test_cli_invalid_file(self):
        """Test error with nonexistent file."""
        result = self.runner.invoke(app, ["--input-json", "nonexistent.json"])
        assert result.exit_code != 0
        # Check for error message (may have formatting/line breaks)
        assert "does not" in result.stdout and "exist" in result.stdout

    def test_cli_full_conversion(self, tmp_path):
        """Test complete conversion workflow."""
        # Create test input file
        input_file = tmp_path / "input.json"
        test_bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": [
                {
                    "resource": {
                        "resourceType": "Patient",
                        "id": "patient-1",
                        "name": [{"family": "Test", "given": ["John"]}],
                        "birthDate": "1980-01-01",
                        "gender": "male"
                    }
                },
                {
                    "resource": {
                        "resourceType": "Encounter",
                        "id": "encounter-1",
                        "status": "finished",
                        "subject": {"reference": "Patient/patient-1"}
                    }
                },
                {
                    "resource": {
                        "resourceType": "Observation",
                        "id": "obs-1",
                        "status": "final",
                        "code": {"coding": [{"code": "12345", "display": "Test"}]},
                        "subject": {"reference": "Patient/patient-1"},
                        "encounter": {"reference": "Encounter/encounter-1"},
                        "effectiveDateTime": "2024-01-01T10:00:00Z"
                    }
                }
            ]
        }
        input_file.write_text(json.dumps(test_bundle))

        # Run conversion
        result = self.runner.invoke(app, [
            "--input-json", str(input_file),
            "--output-dir", str(tmp_path)
        ])

        # Check result
        assert result.exit_code == 0
        assert "SUCCESS" in result.stdout or "Conversion completed" in result.stdout

    def test_cli_with_short_aliases(self, tmp_path):
        """Test using short aliases."""
        # Create test input file
        input_file = tmp_path / "input.json"
        test_bundle = {
            "resourceType": "Bundle",
            "type": "collection",
            "entry": [{
                "resource": {
                    "resourceType": "Patient",
                    "id": "patient-1",
                    "name": [{"family": "Test"}]
                }
            }]
        }
        input_file.write_text(json.dumps(test_bundle))

        # Run conversion with short aliases
        result = self.runner.invoke(app, [
            "-i", str(input_file),
            "-o", str(tmp_path)
        ])

        # Should work the same as long form
        assert result.exit_code == 0

    def test_cli_verbose_flag(self, tmp_path):
        """Test verbose flag."""
        # Create test input
        input_file = tmp_path / "input.json"
        input_file.write_text('{"resourceType": "Bundle", "type": "collection", "entry": []}')

        # Run with verbose flag (short form)
        result = self.runner.invoke(app, [
            "-i", str(input_file),
            "-o", str(tmp_path),
            "-v"
        ])

        # Verbose flag should not cause errors
        assert result.exit_code == 0 or result.exit_code == 1  # May exit with error for empty bundle

    def test_cli_with_config_files(self, tmp_path):
        """Test with configuration files."""
        # Create test input
        input_file = tmp_path / "input.json"
        input_file.write_text('{"resourceType": "Bundle", "type": "collection", "entry": []}')

        # Create minimal config
        placeholder_cfg = tmp_path / "placeholder.json"
        placeholder_cfg.write_text(json.dumps({
            "rechnungsersteller": {"name": "Test", "plz": "12345", "ort": "Test", "strasse": "Test"},
            "leistungserbringer": {"vorname": "Dr.", "name": "Test"},
            "goziffer": {"go": "EBM", "ziffer": "99999", "datum": "2024-01-01"}
        }))

        # Run with config
        result = self.runner.invoke(app, [
            "-i", str(input_file),
            "-o", str(tmp_path),
            "--placeholder-cfg", str(placeholder_cfg)
        ])

        # Should work (may warn about empty bundle but shouldn't crash)
        assert result.exit_code in [0, 1]  # 0 for success, 1 for validation errors

    def test_cli_shell_completion(self):
        """Test that shell completion is available."""
        result = self.runner.invoke(app, ["--help"])
        assert result.exit_code == 0
        # Typer automatically adds these
        assert "--install-completion" in result.stdout
        assert "--show-completion" in result.stdout


# ============================================================================
# RUN CONFIGURATION
# ============================================================================

if __name__ == "__main__":
    # Run with: python test_fhir_to_pad_converter.py
    pytest.main([__file__, "-v", "--tb=short"])
