"""MCP service create_task_tool unit tests."""

import os
from collections.abc import AsyncGenerator
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any

import pytest
from mcp import Client as MCPClient
from pygit2 import Oid, Repository, init_repository
from pygittools.tasks import EMPTY_TREE_OID_HEX, Board, CreateTaskResult, Task, create_task, get_board
from pygittools.tasks_query import TaskView, board_ref

from pygittools_mcp.exceptions import (
	BoardNotFoundError,
	InvalidTaskDueDateError,
	InvalidTaskPriorityError,
	InvalidTaskStatusError,
)
from pygittools_mcp.mcp_server import mcp
from pygittools_mcp.models import CreateTaskToolResult

TAGGER: str = "alice <alice@example.com>"


@pytest.fixture
def repo(tmp_path: Path) -> Repository:
	"""Create a temp repo with a Tasks board so create_task_tool can find it."""
	repo_path = tmp_path / "repo"
	repo_path.mkdir()

	repo = init_repository(str(repo_path), bare=False)

	# Set up initial commit on main
	import subprocess

	os_repo_path = str(repo_path)
	subprocess.run(
		["git", "config", "user.email", "test@test.com"],
		cwd=os_repo_path,
		check=True,
	)
	subprocess.run(
		["git", "config", "user.name", "Test"],
		cwd=os_repo_path,
		check=True,
	)
	with open(os.path.join(os_repo_path, "README.md"), "w") as f:
		f.write("# initial\n")
	subprocess.run(["git", "add", "README.md"], cwd=os_repo_path, check=True)
	subprocess.run(
		["git", "commit", "-m", "initial"],
		cwd=os_repo_path,
		check=True,
		capture_output=True,
		text=True,
	)

	# Create the Tasks board ref so create_task_tool.get_board() can find it
	eoi = Oid(hex=EMPTY_TREE_OID_HEX)
	tasks_board = Board(eoi, "refs/tags/boards/Tasks", TAGGER, description="Tasks")
	tasks_board.write(repo)
	return repo


@pytest.fixture
def test_task(repo: Repository) -> CreateTaskResult:
	board = get_board(repo, board_ref("Tasks"))
	assert board is not None
	return create_task(repo, board, title="Test Task")


@pytest.fixture
async def mcp_client(repo: Repository, monkeypatch: pytest.MonkeyPatch) -> AsyncGenerator[MCPClient, None]:
	monkeypatch.setenv("PYGITTOOLS_REPO", repo.path)
	async with MCPClient(mcp) as client:
		yield client


def assert_default_board(board: dict[str, Any]):
	assert board["name"] == "Tasks"
	assert board["description"] == "Tasks"
	assert board["task_count"] == 0


def assert_is_test_task(view: TaskView, task: CreateTaskResult):
	assert view.get("ref") == task["ref"]
	assert view.get("title") == task["task"].title
	assert view.get("description") == task["task"].description
	view_status = view.get("status")
	task_status = task["task"].status
	assert view_status == task_status.value if task_status else None
	view_priority = view.get("priority")
	task_priority = task["task"].priority
	assert view_priority == task_priority.value if task_priority else None
	view_due_date = view.get("due_date")
	task_due_date = task["task"].due_date
	if view_due_date:
		assert view_due_date == task_due_date.isoformat() if task_due_date else None
	if t := view.get("created_at"):
		assert t == task["task"].created_at.isoformat()
	if t := view.get("updated_at"):
		assert t == task["task"].updated_at.isoformat()


@pytest.mark.anyio
async def test_list_boards_tool(mcp_client: MCPClient) -> None:
	result = await mcp_client.call_tool("list_boards_tool", {"project": None})
	assert not result.is_error, result.content
	content = result.structured_content["result"]
	assert isinstance(content, list)
	assert len(content) == 1
	assert_default_board(content[0])


@pytest.mark.anyio
async def test_list_tasks_tool(mcp_client: MCPClient, test_task: CreateTaskResult) -> None:
	result = await mcp_client.call_tool("list_tasks_tool", {})
	assert not result.is_error, result.content
	content = result.structured_content["result"]
	assert isinstance(content, list)
	assert len(content) == 1
	assert_is_test_task(content[0], test_task)


@pytest.mark.anyio
async def test_get_task_tool(mcp_client: MCPClient, test_task: CreateTaskResult) -> None:
	result = await mcp_client.call_tool("get_task_tool", {"task_ref": test_task["ref"]})
	assert not result.is_error, result.content
	assert_is_test_task(result.structured_content, test_task)


@pytest.mark.anyio
async def test_get_task_tool_not_found(mcp_client: MCPClient) -> None:
	result = await mcp_client.call_tool("get_task_tool", {"task_ref": "fake-task-ref"})
	assert result.is_error


@pytest.mark.anyio
async def test_create_task_tool_success(mcp_client: MCPClient) -> None:
	title = "Test Task"
	description = "A test task"
	create_result = await mcp_client.call_tool("create_task_tool", {"title": title, "description": description})
	assert not create_result.is_error
	task: CreateTaskToolResult = create_result.structured_content
	task_ref = task["ref"]

	get_result = await mcp_client.call_tool("get_task_tool", {"task_ref": task_ref})
	assert not get_result.is_error
	view: TaskView = get_result.structured_content
	assert view.get("ref") == task["ref"]
	assert view.get("title") == title
	assert view.get("description") == description


@pytest.mark.anyio
async def test_create_task_invalid_board(mcp_client: MCPClient) -> None:
	title = "Test Task"
	description = "A test task"
	board = "Fake-Board"
	create_result = await mcp_client.call_tool(
		"create_task_tool", {"title": title, "description": description, "board": board}
	)
	assert create_result.is_error, create_result.content
	assert str(BoardNotFoundError(board)) in create_result.content[0].text


@pytest.mark.anyio
async def test_create_task_invalid_status(mcp_client: MCPClient) -> None:
	title = "Test Task"
	description = "A test task"
	status = "invalid"
	create_result = await mcp_client.call_tool(
		"create_task_tool", {"title": title, "description": description, "status": status}
	)
	assert create_result.is_error
	assert str(InvalidTaskStatusError(status)) in create_result.content[0].text


@pytest.mark.anyio
async def test_create_task_invalid_priority(mcp_client: MCPClient) -> None:
	title = "Test Task"
	description = "A test task"
	priority = "invalid"
	create_result = await mcp_client.call_tool(
		"create_task_tool", {"title": title, "description": description, "priority": priority}
	)
	assert create_result.is_error
	assert str(InvalidTaskPriorityError(priority)) in create_result.content[0].text


@pytest.mark.anyio
async def test_create_task_invalid_due_date(mcp_client: MCPClient):
	title = "Test Task"
	description = "A test task"
	due_date = "soon"
	create_result = await mcp_client.call_tool(
		"create_task_tool", {"title": title, "description": description, "due_date": due_date}
	)
	assert create_result.is_error
	assert str(InvalidTaskDueDateError(due_date)) in create_result.content[0].text


@pytest.mark.anyio
async def test_create_full_task(mcp_client: MCPClient):
	call_args = {
		"title": "Test Task",
		"description": "Test task description",
		"board": "Tasks",
		"status": Task.Status.IN_PROGRESS.value,
		"priority": Task.Priority.HIGH.value,
		"assignee": "ahoyt41",
		"due_duate": (datetime.now() + timedelta(days=5)).isoformat(),
		"tags": '("foo"="bar")',
		"points": 3,
	}
	create_result = await mcp_client.call_tool("create_task_tool", call_args)
	assert not create_result.is_error, create_result.content
	assert create_result.structured_content
	task: CreateTaskToolResult = create_result.structured_content

	get_result = await mcp_client.call_tool("get_task_tool", {"task_ref": task["ref"]})
	assert not get_result.is_error
	assert get_result.structured_content
	view: TaskView = get_result.structured_content

	assert view.get("assignee") == call_args["assignee"]
	assert view.get("description") == call_args["description"]
	assert view.get("status") == call_args["status"]
	assert view.get("priority") == call_args["priority"]
	assert view.get("title") == call_args["title"]
	assert view.get("ref") == task["ref"]