import sys
from pathlib import Path

from docx import Document

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from scripts.import_hopms_dataset import (
    build_dry_run_report,
    import_tables,
    parse_hopms_docx,
)


def _write_sample_doc(path: Path) -> None:
    doc = Document()
    doc.add_paragraph("门急诊业务")
    doc.add_paragraph("患者基本信息")
    doc.add_paragraph("用于对患者在挂号环节产生的挂号数据进行上传。")
    table = doc.add_table(rows=3, cols=6)
    headers = ["字段中文名", "字段名", "类型", "字节", "填报 / 要求", "说明"]
    for idx, header in enumerate(headers):
        table.rows[0].cells[idx].text = header
    table.rows[1].cells[0].text = "医疗机构代码"
    table.rows[1].cells[1].text = "YLJGDM"
    table.rows[1].cells[2].text = "字符串"
    table.rows[1].cells[3].text = "22"
    table.rows[1].cells[4].text = "必填"
    table.rows[1].cells[5].text = "复合主键。医疗机构在HOPMs的唯一识别码"
    table.rows[2].cells[0].text = "挂号时间"
    table.rows[2].cells[1].text = "GHSJ"
    table.rows[2].cells[2].text = "日期时间"
    table.rows[2].cells[3].text = ""
    table.rows[2].cells[4].text = "应填"
    table.rows[2].cells[5].text = "患者挂号时间"

    doc.add_paragraph("CV5501.12 入院病情代码")
    dict_table = doc.add_table(rows=2, cols=3)
    for idx, header in enumerate(["值", "值含义", "说明"]):
        dict_table.rows[0].cells[idx].text = header
    dict_table.rows[1].cells[0].text = "1"
    dict_table.rows[1].cells[1].text = "有"
    dict_table.rows[1].cells[2].text = "示例代码"
    doc.save(path)


def test_parse_hopms_docx_extracts_field_definition_tables(tmp_path):
    docx_path = tmp_path / "sample.docx"
    _write_sample_doc(docx_path)

    tables = parse_hopms_docx(docx_path)

    assert len(tables) == 1
    table = tables[0]
    assert table["table_info"] == {
        "name_zh": "患者基本信息",
        "name_en": "patient_demographics",
        "description": "用于对患者在挂号环节产生的挂号数据进行上传。",
    }
    assert table["columns"][0] == {
        "name_zh": "医疗机构代码",
        "name_en": "YLJGDM",
        "data_type": "varchar(22)",
        "is_primary": True,
        "nullable": False,
        "comment": "复合主键。医疗机构在HOPMs的唯一识别码",
        "requirement": "必填",
        "length": "22",
    }
    assert table["columns"][1]["data_type"] == "timestamp"
    assert table["columns"][1]["nullable"] is True


def test_build_dry_run_report_shapes_backend_payload(tmp_path):
    docx_path = tmp_path / "sample.docx"
    _write_sample_doc(docx_path)
    tables = parse_hopms_docx(docx_path)

    report = build_dry_run_report(tables, source_path=docx_path)

    assert report["summary"]["table_count"] == 1
    assert report["summary"]["column_count"] == 2
    assert report["summary"]["tables_missing_name_en"] == 0
    payload = report["tables"][0]["backend_payload"]
    assert payload["name_zh"] == "患者基本信息"
    assert payload["name_en"] == "patient_demographics"
    assert payload["type"] == "table"
    assert payload["category"] == "HOPMs标准数据集"
    assert payload["parsed_data"] == [
        {
            "name_zh": "医疗机构代码",
            "name_en": "YLJGDM",
            "data_type": "varchar(22)",
            "describe": "复合主键。医疗机构在HOPMs的唯一识别码",
        },
        {
            "name_zh": "挂号时间",
            "name_en": "GHSJ",
            "data_type": "timestamp",
            "describe": "患者挂号时间",
        },
    ]


class _FakeResponse:
    def __init__(self, payload, status_code=200):
        self._payload = payload
        self.status_code = status_code
        self.text = str(payload)

    def raise_for_status(self):
        if self.status_code >= 400:
            raise RuntimeError(f"HTTP {self.status_code}")

    def json(self):
        return self._payload


class _FakeSession:
    def __init__(self):
        self.calls = []

    def post(self, url, json=None, timeout=None):
        self.calls.append({"url": url, "json": json, "timeout": timeout})
        if url.endswith("/api/system/auth/login"):
            return _FakeResponse({"code": 200, "data": {"username": "testuser"}})
        if url.endswith("/api/bd/list"):
            return _FakeResponse({"code": 200, "data": {"records": []}})
        if url.endswith("/api/bd/save"):
            return _FakeResponse(
                {
                    "code": 200,
                    "data": {
                        "id": 123,
                        "name_zh": json["name_zh"],
                        "name_en": json["name_en"],
                    },
                }
            )
        if url.endswith("/api/bd/update"):
            return _FakeResponse(
                {
                    "code": 200,
                    "data": {
                        "id": json["id"],
                        "name_zh": json["name_zh"],
                        "name_en": json["name_en"],
                    },
                }
            )
        raise AssertionError(f"Unexpected URL: {url}")


def test_import_tables_logs_in_and_posts_backend_payload(tmp_path):
    docx_path = tmp_path / "sample.docx"
    _write_sample_doc(docx_path)
    report = build_dry_run_report(parse_hopms_docx(docx_path), source_path=docx_path)
    session = _FakeSession()

    result = import_tables(
        report["tables"],
        base_url="https://example.test/",
        username="testuser",
        password="testpassword123",
        session=session,
        skip_login=False,
    )

    assert result["summary"] == {
        "attempted": 1,
        "succeeded": 1,
        "failed": 0,
        "skipped": 0,
    }
    assert session.calls[0]["url"] == "https://example.test/api/system/auth/login"
    assert session.calls[0]["json"] == {
        "username": "testuser",
        "password": "testpassword123",
    }
    assert session.calls[1]["url"] == "https://example.test/api/bd/list"
    assert session.calls[1]["json"] == {
        "current": 1,
        "size": 10,
        "name_en": "patient_demographics",
    }
    assert session.calls[2]["url"] == "https://example.test/api/bd/save"
    assert session.calls[2]["json"]["name_en"] == "patient_demographics"
    assert session.calls[2]["json"]["parsed_data"][0]["name_en"] == "YLJGDM"


def test_import_tables_can_skip_login_when_endpoint_is_broken(tmp_path):
    docx_path = tmp_path / "sample.docx"
    _write_sample_doc(docx_path)
    report = build_dry_run_report(parse_hopms_docx(docx_path), source_path=docx_path)
    session = _FakeSession()

    result = import_tables(
        report["tables"],
        base_url="https://example.test/",
        username="",
        password="",
        session=session,
        skip_login=True,
    )

    assert result["summary"] == {
        "attempted": 1,
        "succeeded": 1,
        "failed": 0,
        "skipped": 0,
    }
    assert len(session.calls) == 2
    assert session.calls[0]["url"] == "https://example.test/api/bd/list"
    assert session.calls[1]["url"] == "https://example.test/api/bd/save"


class _ExistingDomainSession(_FakeSession):
    def post(self, url, json=None, timeout=None):
        if url.endswith("/api/bd/list"):
            self.calls.append({"url": url, "json": json, "timeout": timeout})
            return _FakeResponse(
                {
                    "code": 200,
                    "data": {
                        "records": [
                            {
                                "id": 456,
                                "name_zh": "患者基本信息",
                                "name_en": "patient_demographics",
                            }
                        ]
                    },
                }
            )
        return super().post(url, json=json, timeout=timeout)


def test_import_tables_skips_existing_domain_by_name_en(tmp_path):
    docx_path = tmp_path / "sample.docx"
    _write_sample_doc(docx_path)
    report = build_dry_run_report(parse_hopms_docx(docx_path), source_path=docx_path)
    session = _ExistingDomainSession()

    result = import_tables(
        report["tables"],
        base_url="https://example.test/",
        session=session,
    )

    assert result["summary"] == {
        "attempted": 1,
        "succeeded": 0,
        "failed": 0,
        "skipped": 1,
    }
    assert len(session.calls) == 1
    assert result["results"][0]["status"] == "skipped"
    assert result["results"][0]["existing_id"] == 456
