"""Tests for xmldev CLI commands."""
from __future__ import annotations

import json
import tempfile
from pathlib import Path

import pytest
from click.testing import CliRunner

from xmldev.cli import main


SCHEMA_DICT = {
    "root": {
        "name": "person",
        "attrs": {"id": {"name": "id", "type": "int", "required": True}},
        "children": [
            {"spec": {"name": "name", "text_type": "string", "aliases": ["fullname"]}, "min_occurs": 1},
            {"spec": {"name": "age", "text_type": "int", "default": 0}, "min_occurs": 0},
        ],
    }
}


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


@pytest.fixture
def schema_file(tmp_path):
    f = tmp_path / "schema.json"
    f.write_text(json.dumps(SCHEMA_DICT))
    return str(f)


@pytest.fixture
def valid_xml_file(tmp_path):
    f = tmp_path / "valid.xml"
    f.write_bytes(b'<person id="1"><name>Bob</name><age>30</age></person>')
    return str(f)


@pytest.fixture
def broken_xml_file(tmp_path):
    f = tmp_path / "broken.xml"
    f.write_bytes(b'<person id="1"><fullname>Bob</fullname></person>')
    return str(f)


# ---------------------------------------------------------------------------
# validate
# ---------------------------------------------------------------------------

def test_validate_valid_xml_exits_0(runner, schema_file, valid_xml_file):
    result = runner.invoke(main, ["validate", "--schema", schema_file, "--input", valid_xml_file])
    assert result.exit_code == 0, result.output


def test_validate_broken_xml_exits_2_after_fix(runner, schema_file, broken_xml_file):
    result = runner.invoke(main, ["validate", "--schema", schema_file, "--input", broken_xml_file])
    # Exit 0 (fully fixed, high confidence alias rename) or 2 (low confidence)
    assert result.exit_code in (0, 2), result.output


def test_validate_writes_audit(runner, schema_file, valid_xml_file, tmp_path):
    audit_path = str(tmp_path / "audit.json")
    result = runner.invoke(main, [
        "validate", "--schema", schema_file, "--input", valid_xml_file, "--audit", audit_path
    ])
    assert result.exit_code == 0
    assert Path(audit_path).exists()
    audit = json.loads(Path(audit_path).read_text())
    assert "summary" in audit


def test_validate_bad_schema_exits_3(runner, tmp_path, valid_xml_file):
    bad_schema = tmp_path / "bad.json"
    bad_schema.write_text('{"not_valid": true}')
    result = runner.invoke(main, ["validate", "--schema", str(bad_schema), "--input", valid_xml_file])
    assert result.exit_code == 3


# ---------------------------------------------------------------------------
# repair
# ---------------------------------------------------------------------------

def test_repair_writes_output(runner, schema_file, broken_xml_file, tmp_path):
    out_path = str(tmp_path / "fixed.xml")
    result = runner.invoke(main, [
        "repair", "--schema", schema_file, "--input", broken_xml_file, "--output", out_path
    ])
    assert result.exit_code in (0, 2)
    assert Path(out_path).exists()
    fixed = Path(out_path).read_text()
    assert "<name>" in fixed


# ---------------------------------------------------------------------------
# lint
# ---------------------------------------------------------------------------

def test_lint_valid_exits_0(runner, schema_file, valid_xml_file):
    result = runner.invoke(main, ["lint", "--schema", schema_file, "--input", valid_xml_file])
    assert result.exit_code == 0


def test_lint_writes_report(runner, schema_file, valid_xml_file, tmp_path):
    report_path = str(tmp_path / "report.json")
    result = runner.invoke(main, [
        "lint", "--schema", schema_file, "--input", valid_xml_file, "--report", report_path
    ])
    assert result.exit_code == 0
    report = json.loads(Path(report_path).read_text())
    assert isinstance(report, list)
    assert report[0]["ok"] is True


def test_lint_directory(runner, schema_file, tmp_path):
    # Create two valid XML files in a directory
    d = tmp_path / "xmlfiles"
    d.mkdir()
    (d / "a.xml").write_bytes(b'<person id="1"><name>A</name></person>')
    (d / "b.xml").write_bytes(b'<person id="2"><name>B</name></person>')
    result = runner.invoke(main, ["lint", "--schema", schema_file, "--input", str(d)])
    assert result.exit_code == 0
    assert "2 file(s)" in result.output
