import importlib
import io
import json
import os

import pytest
from fastapi.testclient import TestClient


@pytest.fixture()
def client(tmp_path, monkeypatch):
    app_mod = importlib.import_module("app")

    app_mod.STORE_DIR = str(tmp_path)
    os.makedirs(app_mod.STORE_DIR, exist_ok=True)

    class DummyRecoApp:
        def __init__(self, name, digit, shift):
            self.name = name
            self.digit = digit
            self.shift = shift

        def start_reconstruction(self, path_raw_data, path_np_data_json, path_order_json):
            import matplotlib.pyplot as plt
            plt.figure()
            plt.plot([0, 1], [0, 1])
            plt.title(f"{self.name}-{self.digit}")
            plt.savefig("data/test.png")
            plt.close()

    monkeypatch.setattr(app_mod, "ReconstructionApp", DummyRecoApp, raising=True)

    return TestClient(app_mod.app)


def _upload(client: TestClient, name: str, content: bytes, mime: str = "application/octet-stream"):
    files = {"file": (name, io.BytesIO(content), mime)}
    r = client.post("/upload", files=files)
    assert r.status_code == 200, r.text
    obj = r.json()
    assert "file_id" in obj
    return obj["file_id"]


def test_upload_json(client: TestClient):
    params = {
        "Np": 8, "Nf": 8, "sl_nb": 1,
        "contrasts": 1, "RF_spoil": 0,
        "ETL": 0, "N_TE": 0, "phi_wing": 0, "N_wings": 0,
        "D_scans": 1
    }
    fid = _upload(client, "params.json", json.dumps(params).encode("utf-8"), "application/json")
    assert isinstance(fid, str) and len(fid) > 0


def test_upload_h5(client: TestClient):
    fake = b"\x89HDF\r\n\x1a\n" + b"\x00" * 64
    fid = _upload(client, "raw.h5", fake, "application/octet-stream")
    assert isinstance(fid, str) and len(fid) > 0


def test_full_reconstruction_flow(client: TestClient, tmp_path):
    raw_bytes = b"\x89HDF\r\n\x1a\n" + b"\x00" * 64
    raw_id = _upload(client, "raw.h5", raw_bytes)

    params = {
        "Np": 8, "Nf": 8, "sl_nb": 1,
        "contrasts": 1, "RF_spoil": 0,
        "ETL": 0, "N_TE": 0, "phi_wing": 0, "N_wings": 0,
        "D_scans": 1
    }
    json_id = _upload(client, "params.json", json.dumps(params).encode("utf-8"), "application/json")

    payload = {
        "file_raw_id": raw_id,
        "file_json_id": json_id,
        "file_order_id": None,
        "sequence_name": "linear_decart",
        "digit": "2d",
        "phase_shift": True
    }
    r = client.post("/reconstruct", json=payload)
    assert r.status_code == 200, r.text
    job = r.json()
    job_id = job["job_id"]

    for _ in range(50):
        s = client.get(f"/jobs/{job_id}")
        assert s.status_code == 200
        st = s.json()
        if st["status"] in ("done", "error"):
            break

    assert st["status"] == "done", f"job failed: {st.get('error_traceback')}"

    lf = client.get(f"/jobs/{job_id}/files")
    assert lf.status_code == 200
    files = lf.json()["files"]
    assert any(name.endswith(".png") for name in files), f"no png files in {files}"

    dz = client.get(f"/jobs/{job_id}/archive.zip")
    assert dz.status_code == 200
    assert dz.headers.get("content-type") == "application/zip"


def test_reconstruct_bad_ids(client: TestClient):
    payload = {
        "file_raw_id": "nope",
        "file_json_id": "nope",
        "file_order_id": None,
        "sequence_name": "linear_decart",
        "digit": "2d",
        "phase_shift": False
    }
    r = client.post("/reconstruct", json=payload)
    assert r.status_code == 400
