"""tests for icdlookup package."""

from __future__ import annotations

import csv
import gzip
import os
import tempfile
from pathlib import Path

import pytest

SAMPLE_ROWS = [
    {
        "icd10_code": "A00",
        "description": "cholera",
        "chapter_desc": "Certain infectious and parasitic diseases (A00-B99)",
        "section_desc": "Intestinal infectious diseases (A00-A09)",
        "category_desc": "cholera",
    },
    {
        "icd10_code": "A00.0",
        "description": "cholera due to vibrio cholerae 01, biovar cholerae",
        "chapter_desc": "Certain infectious and parasitic diseases (A00-B99)",
        "section_desc": "Intestinal infectious diseases (A00-A09)",
        "category_desc": "cholera",
    },
    {
        "icd10_code": "A00.1",
        "description": "cholera due to vibrio cholerae 01, biovar eltor",
        "chapter_desc": "Certain infectious and parasitic diseases (A00-B99)",
        "section_desc": "Intestinal infectious diseases (A00-A09)",
        "category_desc": "cholera",
    },
    {
        "icd10_code": "A00.9",
        "description": "cholera, unspecified",
        "chapter_desc": "Certain infectious and parasitic diseases (A00-B99)",
        "section_desc": "Intestinal infectious diseases (A00-A09)",
        "category_desc": "cholera",
    },
    {
        "icd10_code": "J06.9",
        "description": "acute upper respiratory infection, unspecified",
        "chapter_desc": "Diseases of the respiratory system (J00-J99)",
        "section_desc": "Acute upper respiratory infections (J00-J06)",
        "category_desc": "acute upper respiratory infections of multiple and unspecified sites",
    },
]


@pytest.fixture()
def custom_csv(tmp_path: Path) -> Path:
    """write a small custom reference csv and return its path."""
    p = tmp_path / "custom.csv"
    with open(p, "w", newline="", encoding="utf-8") as fh:
        writer = csv.DictWriter(fh, fieldnames=list(SAMPLE_ROWS[0].keys()))
        writer.writeheader()
        writer.writerows(SAMPLE_ROWS)
    return p


@pytest.fixture()
def custom_csv_gz(tmp_path: Path) -> Path:
    """write a small custom gzip reference csv and return its path."""
    p = tmp_path / "custom.csv.gz"
    with gzip.open(p, "wt", newline="", encoding="utf-8") as fh:
        writer = csv.DictWriter(fh, fieldnames=list(SAMPLE_ROWS[0].keys()))
        writer.writeheader()
        writer.writerows(SAMPLE_ROWS)
    return p


@pytest.fixture(autouse=True)
def _clear_cache():
    """clear the loader cache before each test."""
    from icdlookup.loader import clear_cache
    clear_cache()
    yield
    clear_cache()


class TestLoader:
    def test_normalise_code_basic(self):
        from icdlookup.loader import normalise_code
        assert normalise_code("A00.1") == "A001"
        assert normalise_code("a00.1") == "A001"
        assert normalise_code("  A00.1  ") == "A001"
        assert normalise_code("A001") == "A001"

    def test_add_dot(self):
        from icdlookup.loader import _add_dot
        assert _add_dot("A001") == "A00.1"
        assert _add_dot("A00") == "A00"
        assert _add_dot("J069") == "J06.9"

    def test_load_custom_csv(self, custom_csv: Path):
        from icdlookup.loader import load_data
        rows = load_data(custom_csv)
        assert len(rows) == len(SAMPLE_ROWS)
        assert rows[0]["icd10_code"] == "A00"

    def test_load_custom_csv_gz(self, custom_csv_gz: Path):
        from icdlookup.loader import load_data
        rows = load_data(custom_csv_gz)
        assert len(rows) == len(SAMPLE_ROWS)

    def test_load_data_caching(self, custom_csv: Path):
        from icdlookup.loader import load_data
        rows1 = load_data(custom_csv)
        rows2 = load_data(custom_csv)
        assert rows1 is rows2  # same object (cached)

    def test_load_data_validates_columns(self, tmp_path: Path):
        """a csv missing required columns should raise ValueError."""
        from icdlookup.loader import load_data
        bad = tmp_path / "bad.csv"
        with open(bad, "w", newline="") as fh:
            writer = csv.DictWriter(fh, fieldnames=["code", "desc"])
            writer.writeheader()
            writer.writerow({"code": "A00", "desc": "cholera"})
        with pytest.raises(ValueError, match="missing required column"):
            load_data(bad)

    def test_build_index(self, custom_csv: Path):
        from icdlookup.loader import build_index
        idx = build_index(custom_csv)
        assert "A001" in idx
        assert "A00" in idx
        assert idx["A001"]["description"] == "cholera due to vibrio cholerae 01, biovar eltor"

    def test_load_builtin_dataset(self):
        """the built-in dataset should load without errors."""
        from icdlookup.loader import load_data
        rows = load_data()
        assert len(rows) > 1000  # should have tens of thousands
        assert "icd10_code" in rows[0]
        assert "description" in rows[0]


class TestLookup:
    def test_lookup_basic(self, custom_csv: Path):
        from icdlookup.lookup import lookup
        result = lookup("A00.1", reference=custom_csv)
        assert result == "cholera due to vibrio cholerae 01, biovar eltor"

    def test_lookup_no_dot(self, custom_csv: Path):
        from icdlookup.lookup import lookup
        result = lookup("A001", reference=custom_csv)
        assert result == "cholera due to vibrio cholerae 01, biovar eltor"

    def test_lookup_case_insensitive(self, custom_csv: Path):
        from icdlookup.lookup import lookup
        result = lookup("a00.1", reference=custom_csv)
        assert result is not None
        assert "cholera" in result

    def test_lookup_not_found(self, custom_csv: Path):
        from icdlookup.lookup import lookup
        assert lookup("Z99.99", reference=custom_csv) is None

    def test_lookup_detail(self, custom_csv: Path):
        from icdlookup.lookup import lookup
        result = lookup("A00.1", detail=True, reference=custom_csv)
        assert isinstance(result, dict)
        assert result["icd10_code"] == "A00.1"
        assert "chapter_desc" in result

    def test_lookup_builtin(self):
        """lookup using the built-in dataset."""
        from icdlookup.lookup import lookup
        result = lookup("A00.1")
        assert result is not None
        assert "cholera" in result.lower()


class TestSearch:
    def test_search_basic(self, custom_csv: Path):
        from icdlookup.lookup import search
        results = search("cholera", reference=custom_csv)
        assert len(results) >= 1
        codes = [r[0] for r in results]
        assert "A00" in codes
        assert "A00.1" in codes

    def test_search_case_insensitive(self, custom_csv: Path):
        from icdlookup.lookup import search
        results = search("CHOLERA", reference=custom_csv)
        assert len(results) >= 1

    def test_search_no_results(self, custom_csv: Path):
        from icdlookup.lookup import search
        results = search("xyznonexistent", reference=custom_csv)
        assert results == []

    def test_search_limit(self, custom_csv: Path):
        from icdlookup.lookup import search
        results = search("cholera", limit=2, reference=custom_csv)
        assert len(results) <= 2

    def test_search_builtin(self):
        from icdlookup.lookup import search
        results = search("cholera")
        assert len(results) >= 1
        assert any("cholera" in desc.lower() for _, desc in results)


class TestAnnotate:
    def test_annotate_basic(self, custom_csv: Path):
        import pandas as pd
        from icdlookup.lookup import annotate

        df = pd.DataFrame({"icd_code": ["A00.1", "J06.9", "Z99.99"]})
        result = annotate(df, "icd_code", reference=custom_csv)

        assert "icd_description" in result.columns
        assert result.loc[0, "icd_description"] == "cholera due to vibrio cholerae 01, biovar eltor"
        assert result.loc[1, "icd_description"] == "acute upper respiratory infection, unspecified"
        assert result.loc[2, "icd_description"] is None

    def test_annotate_custom_column_name(self, custom_csv: Path):
        import pandas as pd
        from icdlookup.lookup import annotate

        df = pd.DataFrame({"code": ["A00"]})
        result = annotate(
            df, "code",
            description_column="desc",
            reference=custom_csv,
        )
        assert "desc" in result.columns
        assert result.loc[0, "desc"] == "cholera"

    def test_annotate_does_not_modify_original(self, custom_csv: Path):
        import pandas as pd
        from icdlookup.lookup import annotate

        df = pd.DataFrame({"icd_code": ["A00.1"]})
        original_cols = list(df.columns)
        _ = annotate(df, "icd_code", reference=custom_csv)
        assert list(df.columns) == original_cols  # original unchanged

    def test_annotate_no_dot_codes(self, custom_csv: Path):
        import pandas as pd
        from icdlookup.lookup import annotate

        df = pd.DataFrame({"icd_code": ["A001"]})
        result = annotate(df, "icd_code", reference=custom_csv)
        assert result.loc[0, "icd_description"] == "cholera due to vibrio cholerae 01, biovar eltor"


class TestCLI:
    def test_cli_lookup(self, custom_csv: Path, capsys):
        from icdlookup.cli import main
        main(["A00.1", "--reference", str(custom_csv)])
        out = capsys.readouterr().out
        assert "cholera" in out.lower()

    def test_cli_lookup_detail(self, custom_csv: Path, capsys):
        from icdlookup.cli import main
        main(["A00.1", "--detail", "--reference", str(custom_csv)])
        out = capsys.readouterr().out
        assert "chapter_desc" in out

    def test_cli_search(self, custom_csv: Path, capsys):
        from icdlookup.cli import main
        main(["--search", "cholera", "--reference", str(custom_csv)])
        out = capsys.readouterr().out
        assert "A00" in out

    def test_cli_not_found(self, custom_csv: Path):
        from icdlookup.cli import main
        with pytest.raises(SystemExit, match="1"):
            main(["Z99.99", "--reference", str(custom_csv)])

    def test_cli_version(self, capsys):
        from icdlookup.cli import main
        main(["--version"])
        out = capsys.readouterr().out
        assert "icdlookup" in out

    def test_cli_annotate_file(self, custom_csv: Path, tmp_path: Path, capsys):
        """test the --file flag to annotate a csv."""
        input_csv = tmp_path / "input.csv"
        output_csv = tmp_path / "output.csv"

        with open(input_csv, "w", newline="") as fh:
            writer = csv.DictWriter(fh, fieldnames=["icd_code"])
            writer.writeheader()
            writer.writerow({"icd_code": "A00.1"})
            writer.writerow({"icd_code": "J06.9"})

        from icdlookup.cli import main
        main([
            "--file", str(input_csv),
            "--column", "icd_code",
            "--output", str(output_csv),
            "--reference", str(custom_csv),
        ])

        assert output_csv.exists()
        with open(output_csv) as fh:
            reader = csv.DictReader(fh)
            rows = list(reader)
        assert len(rows) == 2
        assert "icd_description" in rows[0]
        assert "cholera" in rows[0]["icd_description"]
