"""Unit tests for CLI module."""

import json
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest
from click.testing import CliRunner

from flat_fhir.cli import main

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


@pytest.fixture
def runner():
    return CliRunner()


def test_convert(runner, tmp_path):
    out = str(tmp_path / "output.xlsx")
    result = runner.invoke(main, ["convert", str(SAMPLE), "-o", out])
    assert result.exit_code == 0
    assert "Wrote" in result.output
    assert Path(out).exists()


def test_convert_with_sheets(runner, tmp_path):
    out = str(tmp_path / "output.xlsx")
    result = runner.invoke(main, ["convert", str(SAMPLE), "-o", out, "--sheets", "patient,vitals"])
    assert result.exit_code == 0


def test_convert_nonexistent_file(runner):
    result = runner.invoke(main, ["convert", "nonexistent.json"])
    assert result.exit_code != 0


def test_unflatten(runner, tmp_path):
    # First create XLSX
    xlsx = str(tmp_path / "input.xlsx")
    runner.invoke(main, ["convert", str(SAMPLE), "-o", xlsx])

    out_json = str(tmp_path / "bundle.json")
    result = runner.invoke(main, ["unflatten", xlsx, "-o", out_json])
    assert result.exit_code == 0
    assert "Wrote" in result.output
    assert Path(out_json).exists()


def test_merge(runner, tmp_path):
    out = str(tmp_path / "merged.xlsx")
    result = runner.invoke(main, ["merge", str(SAMPLE), str(SAMPLE), "-o", out])
    assert result.exit_code == 0
    assert "Merged" in result.output
    assert Path(out).exists()


def test_merge_too_few_files(runner):
    result = runner.invoke(main, ["merge", str(SAMPLE)])
    assert result.exit_code != 0
    assert "at least 2" in result.output


def test_version(runner):
    result = runner.invoke(main, ["--version"])
    assert result.exit_code == 0
    assert "0.1.0" in result.output


def test_help(runner):
    result = runner.invoke(main, ["--help"])
    assert result.exit_code == 0
    assert "flat-fhir" in result.output


def test_fetch_patient_url(runner, tmp_path):
    """Test fetch command with a Patient URL (triggers $everything)."""
    with open(SAMPLE, encoding="utf-8") as f:
        sample_data = json.load(f)

    mock_response = MagicMock()
    mock_response.json.return_value = sample_data
    mock_response.raise_for_status = MagicMock()

    out = str(tmp_path / "fetched.xlsx")
    with patch("requests.get", return_value=mock_response) as mock_get:
        result = runner.invoke(main, ["fetch", "http://fhir.example.com/Patient/123", "-o", out])
        assert result.exit_code == 0
        assert "Wrote" in result.output
        # Should have appended $everything
        mock_get.assert_called_once()
        call_url = mock_get.call_args[0][0]
        assert "$everything" in call_url


def test_fetch_non_patient_url(runner, tmp_path):
    """Test fetch command with a non-Patient URL."""
    with open(SAMPLE, encoding="utf-8") as f:
        sample_data = json.load(f)

    mock_response = MagicMock()
    mock_response.json.return_value = sample_data
    mock_response.raise_for_status = MagicMock()

    out = str(tmp_path / "fetched.xlsx")
    with patch("requests.get", return_value=mock_response) as mock_get:
        result = runner.invoke(main, ["fetch", "http://fhir.example.com/Bundle/123", "-o", out])
        assert result.exit_code == 0
        call_url = mock_get.call_args[0][0]
        assert "$everything" not in call_url


def test_fetch_with_auth(runner, tmp_path):
    """Test fetch with auth token."""
    with open(SAMPLE, encoding="utf-8") as f:
        sample_data = json.load(f)

    mock_response = MagicMock()
    mock_response.json.return_value = sample_data
    mock_response.raise_for_status = MagicMock()

    out = str(tmp_path / "fetched.xlsx")
    with patch("requests.get", return_value=mock_response) as mock_get:
        result = runner.invoke(main, ["fetch", "http://fhir.example.com/Bundle/1",
                                       "-o", out, "--auth", "mytoken123"])
        assert result.exit_code == 0
        headers = mock_get.call_args[1]["headers"]
        assert headers["Authorization"] == "Bearer mytoken123"


def test_fetch_with_sheets(runner, tmp_path):
    """Test fetch with --sheets filter."""
    with open(SAMPLE, encoding="utf-8") as f:
        sample_data = json.load(f)

    mock_response = MagicMock()
    mock_response.json.return_value = sample_data
    mock_response.raise_for_status = MagicMock()

    out = str(tmp_path / "fetched.xlsx")
    with patch("requests.get", return_value=mock_response):
        result = runner.invoke(main, ["fetch", "http://fhir.example.com/Bundle/1",
                                       "-o", out, "--sheets", "patient,vitals"])
        assert result.exit_code == 0
