"""
階段 2:任務 + 專案 API 整合測試。

測試範圍:
  - 任務 CRUD(create / list / get / patch / delete)
  - 任務 filter(status / mine / project_id)
  - status → done 自動填 completed_at,反向自動清空
  - 專案 CRUD + 權限(只有 owner 能修改/刪除別人建的專案)
  - 軟刪除:刪掉的任務/專案不會出現在 list
"""

import pytest
import pytest_asyncio
from httpx import AsyncClient


# =============================================================================
# 共用 fixtures
# =============================================================================
@pytest_asyncio.fixture
async def owner_token(client: AsyncClient):
    """註冊 owner(第一個帳號自動成為 owner)並回傳 access token。"""
    await client.post(
        "/api/auth/register",
        json={"email": "owner@x.com", "password": "ownerpass", "display_name": "Owner"},
    )
    res = await client.post(
        "/api/auth/login",
        json={"email": "owner@x.com", "password": "ownerpass"},
    )
    return res.json()["tokens"]["access_token"]


@pytest_asyncio.fixture
async def member_token(client: AsyncClient, owner_token):
    """先有 owner,再註冊一個 member 帳號(順序很重要)。"""
    await client.post(
        "/api/auth/register",
        json={"email": "member@x.com", "password": "memberpass", "display_name": "Member"},
    )
    res = await client.post(
        "/api/auth/login",
        json={"email": "member@x.com", "password": "memberpass"},
    )
    return res.json()["tokens"]["access_token"]


def auth(token):
    """組 Authorization header 的 helper。"""
    return {"Authorization": f"Bearer {token}"}


# =============================================================================
# 任務:基本 CRUD
# =============================================================================
async def test_create_task_minimal(client: AsyncClient, owner_token):
    """只給 title 也能建任務,其他欄位用預設。"""
    res = await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "First task"},
    )
    assert res.status_code == 201, res.text
    body = res.json()
    assert body["title"] == "First task"
    assert body["status"] == "todo"
    assert body["priority"] == "medium"
    assert body["due_date"] is None
    assert body["completed_at"] is None
    assert body["creator"]["display_name"] == "Owner"


async def test_create_task_full(client: AsyncClient, owner_token):
    res = await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={
            "title": "Full task",
            "description": "Detailed description",
            "status": "in_progress",
            "priority": "high",
            "due_date": "2026-12-31",
        },
    )
    assert res.status_code == 201
    body = res.json()
    assert body["description"] == "Detailed description"
    assert body["status"] == "in_progress"
    assert body["priority"] == "high"
    assert body["due_date"] == "2026-12-31"


async def test_create_task_done_auto_fills_completed_at(client: AsyncClient, owner_token):
    """建任務時若一開就是 done,要自動記下 completed_at。"""
    res = await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "Already done", "status": "done"},
    )
    assert res.status_code == 201
    assert res.json()["completed_at"] is not None


async def test_create_task_requires_auth(client: AsyncClient):
    res = await client.post("/api/tasks", json={"title": "anonymous task"})
    assert res.status_code == 401


async def test_list_tasks_empty(client: AsyncClient, owner_token):
    res = await client.get("/api/tasks", headers=auth(owner_token))
    assert res.status_code == 200
    assert res.json() == []


async def test_list_tasks_returns_created(client: AsyncClient, owner_token):
    await client.post("/api/tasks", headers=auth(owner_token), json={"title": "A"})
    await client.post("/api/tasks", headers=auth(owner_token), json={"title": "B"})
    res = await client.get("/api/tasks", headers=auth(owner_token))
    assert len(res.json()) == 2


async def test_get_single_task(client: AsyncClient, owner_token):
    create_res = await client.post(
        "/api/tasks", headers=auth(owner_token), json={"title": "Solo"}
    )
    task_id = create_res.json()["id"]
    res = await client.get(f"/api/tasks/{task_id}", headers=auth(owner_token))
    assert res.status_code == 200
    assert res.json()["title"] == "Solo"


async def test_get_nonexistent_task_404(client: AsyncClient, owner_token):
    fake_id = "00000000-0000-0000-0000-000000000000"
    res = await client.get(f"/api/tasks/{fake_id}", headers=auth(owner_token))
    assert res.status_code == 404


# =============================================================================
# 任務:PATCH(狀態切換 + completed_at 自動化)
# =============================================================================
async def test_patch_task_to_done_fills_completed_at(client: AsyncClient, owner_token):
    create = await client.post(
        "/api/tasks", headers=auth(owner_token), json={"title": "Will done"}
    )
    task_id = create.json()["id"]
    assert create.json()["completed_at"] is None

    patch = await client.patch(
        f"/api/tasks/{task_id}",
        headers=auth(owner_token),
        json={"status": "done"},
    )
    assert patch.status_code == 200
    assert patch.json()["status"] == "done"
    assert patch.json()["completed_at"] is not None


async def test_patch_done_back_to_todo_clears_completed_at(client: AsyncClient, owner_token):
    create = await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "Toggle", "status": "done"},
    )
    task_id = create.json()["id"]
    assert create.json()["completed_at"] is not None

    patch = await client.patch(
        f"/api/tasks/{task_id}",
        headers=auth(owner_token),
        json={"status": "todo"},
    )
    assert patch.status_code == 200
    assert patch.json()["completed_at"] is None


async def test_patch_only_updates_provided_fields(client: AsyncClient, owner_token):
    """PATCH semantics:只改有送的欄位,其他保持。"""
    create = await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "Original", "priority": "high"},
    )
    task_id = create.json()["id"]
    patch = await client.patch(
        f"/api/tasks/{task_id}",
        headers=auth(owner_token),
        json={"description": "added description"},
    )
    assert patch.status_code == 200
    assert patch.json()["title"] == "Original"
    assert patch.json()["priority"] == "high"
    assert patch.json()["description"] == "added description"


# =============================================================================
# 任務:filter
# =============================================================================
async def test_filter_by_status(client: AsyncClient, owner_token):
    await client.post("/api/tasks", headers=auth(owner_token), json={"title": "T1", "status": "todo"})
    await client.post("/api/tasks", headers=auth(owner_token), json={"title": "T2", "status": "todo"})
    await client.post("/api/tasks", headers=auth(owner_token), json={"title": "D1", "status": "done"})

    todos = await client.get("/api/tasks?status=todo", headers=auth(owner_token))
    assert len(todos.json()) == 2

    dones = await client.get("/api/tasks?status=done", headers=auth(owner_token))
    assert len(dones.json()) == 1


async def test_filter_mine_includes_only_my_tasks(client: AsyncClient, owner_token, member_token):
    """owner 跟 member 各建一個任務,?mine=true 只看自己的。"""
    await client.post("/api/tasks", headers=auth(owner_token), json={"title": "Owner task"})
    await client.post("/api/tasks", headers=auth(member_token), json={"title": "Member task"})

    owner_mine = await client.get("/api/tasks?mine=true", headers=auth(owner_token))
    titles = [t["title"] for t in owner_mine.json()]
    assert "Owner task" in titles
    assert "Member task" not in titles


# =============================================================================
# 任務:DELETE(軟刪除 + 權限)
# =============================================================================
async def test_delete_task_soft_removes_from_list(client: AsyncClient, owner_token):
    create = await client.post("/api/tasks", headers=auth(owner_token), json={"title": "Bye"})
    task_id = create.json()["id"]
    delete = await client.delete(f"/api/tasks/{task_id}", headers=auth(owner_token))
    assert delete.status_code == 204
    res = await client.get("/api/tasks", headers=auth(owner_token))
    assert all(t["id"] != task_id for t in res.json())


async def test_member_cannot_delete_others_task(client: AsyncClient, owner_token, member_token):
    """member 不能刪 owner 建立的任務(只有建立者、被指派的、或 owner 能刪)。"""
    create = await client.post(
        "/api/tasks", headers=auth(owner_token), json={"title": "Owner only"}
    )
    task_id = create.json()["id"]
    delete = await client.delete(f"/api/tasks/{task_id}", headers=auth(member_token))
    assert delete.status_code == 403


async def test_owner_can_delete_anyone_task(client: AsyncClient, owner_token, member_token):
    """公司 owner 有權刪 member 建立的任務。"""
    create = await client.post(
        "/api/tasks", headers=auth(member_token), json={"title": "Member task"}
    )
    task_id = create.json()["id"]
    delete = await client.delete(f"/api/tasks/{task_id}", headers=auth(owner_token))
    assert delete.status_code == 204


# =============================================================================
# 專案
# =============================================================================
async def test_create_and_list_project(client: AsyncClient, owner_token):
    await client.post(
        "/api/projects",
        headers=auth(owner_token),
        json={"name": "Coba", "color": "#1F4E79"},
    )
    res = await client.get("/api/projects", headers=auth(owner_token))
    assert res.status_code == 200
    assert len(res.json()) == 1
    assert res.json()[0]["name"] == "Coba"


async def test_member_cannot_modify_others_project(client: AsyncClient, owner_token, member_token):
    """member 不能改 owner 建立的專案。"""
    create = await client.post(
        "/api/projects",
        headers=auth(owner_token),
        json={"name": "Owner Project"},
    )
    project_id = create.json()["id"]
    patch = await client.patch(
        f"/api/projects/{project_id}",
        headers=auth(member_token),
        json={"name": "Hijacked"},
    )
    assert patch.status_code == 403


async def test_task_with_project_id_links_correctly(client: AsyncClient, owner_token):
    proj = await client.post(
        "/api/projects",
        headers=auth(owner_token),
        json={"name": "P"},
    )
    project_id = proj.json()["id"]
    task = await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "Inside project", "project_id": project_id},
    )
    assert task.status_code == 201
    assert task.json()["project_id"] == project_id

    # filter by project
    res = await client.get(
        f"/api/tasks?project_id={project_id}", headers=auth(owner_token)
    )
    assert len(res.json()) == 1
    assert res.json()[0]["project_id"] == project_id


async def test_filter_inbox_tasks(client: AsyncClient, owner_token):
    """project_id=null filter 只看 inbox(無專案)任務。"""
    proj = await client.post(
        "/api/projects", headers=auth(owner_token), json={"name": "P"}
    )
    pid = proj.json()["id"]
    await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "in project", "project_id": pid},
    )
    await client.post(
        "/api/tasks",
        headers=auth(owner_token),
        json={"title": "in inbox"},
    )
    inbox = await client.get(
        "/api/tasks?project_id=null", headers=auth(owner_token)
    )
    titles = [t["title"] for t in inbox.json()]
    assert titles == ["in inbox"]
