import json
import time

import text_extract_service as service


class FakeRedis:
    def __init__(self):
        self.data = {}

    def set(self, key, value):
        self.data[key] = value
        return True

    def get(self, key):
        return self.data.get(key)


class BrokenRedis:
    def set(self, key, value):
        raise TimeoutError("redis timeout")

    def get(self, key):
        raise TimeoutError("redis timeout")


def fake_extract_events_from_url(url):
    yield sse_event({"message": {"url": url}, "type": 5})
    yield sse_event({"message": "第一段", "type": 1})
    yield sse_event({"message": "第二段", "type": 1})


def sse_event(data):
    return f"data: {json.dumps(data, ensure_ascii=False)}\n\n".encode("utf-8")


def wait_task_done(client, task_id):
    for _ in range(20):
        response = client.get(f"/api/text-extract/tasks/{task_id}")
        task = response.get_json()["data"]
        if task["status"] in ("success", "failed"):
            return task
        time.sleep(0.05)
    raise AssertionError("task did not finish in time")


def test_submit_multiple_urls_and_query_result(monkeypatch):
    monkeypatch.setattr(service, "extract_events_from_url", fake_extract_events_from_url)
    monkeypatch.setattr(service, "redis_client", FakeRedis())
    service.tasks.clear()

    client = service.app.test_client()
    response = client.post(
        "/api/text-extract/tasks",
        json={
            "urls": [
                "https://example.com/video-a",
                "https://example.com/video-b",
            ]
        },
    )

    assert response.status_code == 202
    body = response.get_json()
    assert body["code"] == 200
    assert len(body["data"]) == 2
    assert body["data"][0]["task_id"] != body["data"][1]["task_id"]

    task = wait_task_done(client, body["data"][0]["task_id"])
    assert task["status"] == "success"
    assert task["status_message"] == "文案提取完成"
    assert task["text"] == "第一段第二段"
    assert task["video_info"] == {"url": "https://example.com/video-a"}
    assert "events" not in task


def test_get_result_by_query_task_id(monkeypatch):
    monkeypatch.setattr(service, "extract_events_from_url", fake_extract_events_from_url)
    monkeypatch.setattr(service, "redis_client", FakeRedis())
    service.tasks.clear()

    client = service.app.test_client()
    submit_response = client.post(
        "/api/text-extract/tasks",
        json={"url": "https://example.com/video"},
    )
    task_id = submit_response.get_json()["data"][0]["task_id"]
    wait_task_done(client, task_id)

    response = client.get("/api/text-extract/result", query_string={"task_id": task_id})

    assert response.status_code == 200
    body = response.get_json()
    assert body["data"]["task_id"] == task_id
    assert body["data"]["status_message"] == "文案提取完成"
    assert body["data"]["text"] == "第一段第二段"
    assert body["data"]["video_info"] == {"url": "https://example.com/video"}
    assert "events" not in body["data"]


def test_get_result_after_memory_cache_is_cleared(monkeypatch):
    monkeypatch.setattr(service, "extract_events_from_url", fake_extract_events_from_url)
    monkeypatch.setattr(service, "redis_client", FakeRedis())
    service.tasks.clear()

    client = service.app.test_client()
    submit_response = client.post(
        "/api/text-extract/tasks",
        json={"url": "https://example.com/video"},
    )
    task_id = submit_response.get_json()["data"][0]["task_id"]
    wait_task_done(client, task_id)

    service.tasks.clear()
    response = client.get(f"/api/text-extract/tasks/{task_id}")

    assert response.status_code == 200
    body = response.get_json()
    assert body["data"]["task_id"] == task_id
    assert body["data"]["text"] == "第一段第二段"


def test_submit_still_returns_when_redis_fails(monkeypatch):
    monkeypatch.setattr(service, "extract_events_from_url", fake_extract_events_from_url)
    monkeypatch.setattr(service, "redis_client", BrokenRedis())
    service.tasks.clear()

    client = service.app.test_client()
    response = client.post(
        "/api/text-extract/tasks",
        json={"url": "https://example.com/video"},
    )

    assert response.status_code == 202
    body = response.get_json()
    assert body["code"] == 200
    assert len(body["data"]) == 1
    assert body["data"][0]["task_id"]
