"""Mocked provider client tests (no real network calls)."""

from __future__ import annotations

from pathlib import Path
from typing import Any

import pytest

from app.services.integrations.providers.github_issues import GitHubIssuesProvider
from app.services.integrations.providers.google_tasks import GoogleTasksProvider
from app.services.integrations.providers.microsoft_todo import MicrosoftTodoProvider
from app.services.integrations.providers.notion import NotionNotesProvider
from app.services.integrations.providers.todoist import TodoistTaskProvider
from app.services.integrations.providers.trello import TrelloTaskProvider
from app.services.integrations.providers.vault_local import VaultLocalNotesProvider


class MockResponse:
    """Minimal requests.Response-like object for provider tests."""

    def __init__(self, status_code: int, payload: Any, text: str | None = None):
        self.status_code = status_code
        self._payload = payload
        self.text = text if text is not None else ("" if payload is None else str(payload))

    def json(self) -> Any:
        return self._payload


def test_todoist_provider_adapter(monkeypatch: pytest.MonkeyPatch) -> None:
    """Todoist adapter should map list/create/complete using service methods."""
    from app.services import todoist_service

    monkeypatch.setattr(
        todoist_service,
        "list_tasks",
        lambda: [{"id": "1", "content": "Task A", "due": {"date": "2026-03-01"}}],
    )
    monkeypatch.setattr(
        todoist_service,
        "create_task",
        lambda content, description=None, due_string=None: {"id": "2", "content": content, "description": description},
    )
    completed: list[str] = []
    monkeypatch.setattr(todoist_service, "complete_task", lambda task_id: completed.append(str(task_id)))

    provider = TodoistTaskProvider()
    listed = provider.list_tasks()
    assert listed[0].content == "Task A"

    created = provider.create_task(content="Task B", description="desc")
    assert created.id == "2"
    provider.complete_task(task_id="2")
    assert completed == ["2"]


def test_trello_provider_with_mocked_requests(monkeypatch: pytest.MonkeyPatch) -> None:
    """Trello provider should call list/create/complete/test routes."""
    from app.services.integrations.providers import trello as trello_module

    def fake_request(method: str, url: str, params: dict[str, Any] | None = None, **kwargs: Any) -> MockResponse:
        if method == "GET" and "/lists/list_1/cards" in url:
            return MockResponse(200, [{"id": "card_1", "name": "Card A", "desc": "", "closed": False}])
        if method == "POST" and url.endswith("/cards"):
            return MockResponse(200, {"id": "card_2", "name": params.get("name", "Card B"), "desc": "", "closed": False})
        if method == "PUT" and "/cards/card_1/closed" in url:
            return MockResponse(200, {"id": "card_1", "closed": True})
        if method == "GET" and url.endswith("/members/me"):
            return MockResponse(200, {"username": "trello-user"})
        raise AssertionError(f"Unexpected Trello request: {method} {url}")

    monkeypatch.setattr(trello_module.requests, "request", fake_request)

    provider = TrelloTaskProvider(api_key="k", token="t", list_id="list_1")
    tasks = provider.list_tasks()
    assert tasks[0].content == "Card A"
    created = provider.create_task(content="Card B")
    assert created.id == "card_2"
    provider.complete_task(task_id="card_1")
    assert provider.test_connection().ok is True


def test_github_issues_provider_with_mocked_requests(monkeypatch: pytest.MonkeyPatch) -> None:
    """GitHub Issues provider should support tasks and notes operations."""
    from app.services.integrations.providers import github_issues as github_module

    def fake_request(method: str, url: str, json: dict[str, Any] | None = None, **kwargs: Any) -> MockResponse:
        if method == "GET" and "/repos/owner/repo/issues" in url and "/search/issues" not in url:
            return MockResponse(200, [{"number": 1, "title": "Issue A", "body": "body", "state": "open"}])
        if method == "POST" and url.endswith("/repos/owner/repo/issues"):
            return MockResponse(201, {"number": 2, "title": json.get("title", ""), "body": json.get("body", ""), "state": "open"})
        if method == "PATCH" and "/repos/owner/repo/issues/1" in url:
            return MockResponse(200, {"number": 1, "state": "closed"})
        if method == "GET" and "/search/issues" in url:
            return MockResponse(
                200,
                {"items": [{"number": 4, "title": "Match", "body": "snippet", "html_url": "https://github.com/owner/repo/issues/4"}]},
            )
        if method == "POST" and url.endswith("/repos/owner/repo/issues/1/comments"):
            return MockResponse(201, {"id": 10})
        if method == "GET" and url.endswith("/repos/owner/repo"):
            return MockResponse(200, {"full_name": "owner/repo"})
        raise AssertionError(f"Unexpected GitHub request: {method} {url}")

    monkeypatch.setattr(github_module.requests, "request", fake_request)

    provider = GitHubIssuesProvider(token="github_token_placeholder", repo="owner/repo")
    assert provider.list_tasks()[0].content == "Issue A"
    assert provider.create_task(content="Issue B").id == "2"
    provider.complete_task(task_id="1")
    assert provider.search(query="match")[0].title == "Match"
    assert provider.create_note(title="N", content="C") == "2"
    provider.append_note(target_id="1", content="append")
    assert provider.test_connection().ok is True


def test_notion_provider_with_mocked_requests(monkeypatch: pytest.MonkeyPatch) -> None:
    """Notion provider should support search/create/append/test."""
    from app.services.integrations.providers import notion as notion_module

    def fake_request(method: str, url: str, json: dict[str, Any] | None = None, **kwargs: Any) -> MockResponse:
        if method == "POST" and url.endswith("/search"):
            return MockResponse(
                200,
                {"results": [{"id": "page_1", "url": "https://notion.so/page_1", "properties": {"Name": {"title": [{"plain_text": "Doc"}]}}}]},
            )
        if method == "POST" and url.endswith("/pages"):
            return MockResponse(200, {"id": "page_new"})
        if method == "PATCH" and "/blocks/" in url and "/children" in url:
            return MockResponse(200, {"results": []})
        if method == "GET" and url.endswith("/users/me"):
            return MockResponse(200, {"object": "user"})
        raise AssertionError(f"Unexpected Notion request: {method} {url}")

    monkeypatch.setattr(notion_module.requests, "request", fake_request)

    provider = NotionNotesProvider(token="notion_token_placeholder", page_id="page_1")
    assert provider.search(query="doc")[0].title == "Doc"
    note_id = provider.create_note(title="Title", content="Body")
    assert note_id == "page_new"
    provider.append_note(target_id=note_id, content="More")
    assert provider.test_connection().ok is True


def test_google_tasks_provider_with_mocked_requests(monkeypatch: pytest.MonkeyPatch) -> None:
    """Google Tasks provider should support list/create/complete/update/test."""
    from app.services.integrations.providers import google_tasks as google_module

    def fake_request(method: str, url: str, headers: dict[str, Any] | None = None, json: dict[str, Any] | None = None, **kwargs: Any) -> MockResponse:
        if method == "GET" and "/lists/@default/tasks" in url:
            return MockResponse(200, {"items": [{"id": "g1", "title": "GT", "status": "needsAction", "due": "2026-03-01T00:00:00.000Z"}]})
        if method == "POST" and "/lists/@default/tasks" in url:
            return MockResponse(200, {"id": "g2", "title": json.get("title", "")})
        if method == "PATCH" and "/lists/@default/tasks/" in url:
            return MockResponse(200, {"id": "g1", "title": "GT", "due": "2026-03-02T00:00:00.000Z"})
        if method == "GET" and "/users/@me/lists" in url:
            return MockResponse(200, {"items": [{"id": "@default"}]})
        raise AssertionError(f"Unexpected Google request: {method} {url}")

    monkeypatch.setattr(google_module.requests, "request", fake_request)

    provider = GoogleTasksProvider(access_token="access", task_list_id="@default")
    assert provider.list_tasks()[0].content == "GT"
    assert provider.create_task(content="New GT").id == "g2"
    provider.complete_task(task_id="g1")
    assert provider.update_due(task_id="g1", due_string="2026-03-02") is not None
    assert provider.test_connection().ok is True


def test_microsoft_todo_provider_with_mocked_requests(monkeypatch: pytest.MonkeyPatch) -> None:
    """Microsoft To Do provider should support list/create/complete/test."""
    from app.services.integrations.providers import microsoft_todo as ms_module

    def fake_request(method: str, url: str, headers: dict[str, Any] | None = None, json: dict[str, Any] | None = None, **kwargs: Any) -> MockResponse:
        if method == "GET" and url.endswith("/me/todo/lists"):
            return MockResponse(200, {"value": [{"id": "list_1"}]})
        if method == "GET" and url.endswith("/me/todo/lists/list_1/tasks"):
            return MockResponse(200, {"value": [{"id": "m1", "title": "MS Task", "status": "notStarted"}]})
        if method == "POST" and url.endswith("/me/todo/lists/list_1/tasks"):
            return MockResponse(201, {"id": "m2", "title": json.get("title", "")})
        if method == "PATCH" and "/me/todo/lists/list_1/tasks/" in url:
            return MockResponse(200, {"id": "m1", "title": "MS Task", "status": "completed"})
        if method == "GET" and url.endswith("/me"):
            return MockResponse(200, {"displayName": "MS User"})
        raise AssertionError(f"Unexpected Microsoft request: {method} {url}")

    monkeypatch.setattr(ms_module.requests, "request", fake_request)

    provider = MicrosoftTodoProvider(access_token="access")
    assert provider.list_tasks()[0].content == "MS Task"
    assert provider.create_task(content="MS New").id == "m2"
    provider.complete_task(task_id="m1")
    assert provider.test_connection().ok is True


def test_vault_local_provider(tmp_path: Path) -> None:
    """Vault local provider should create notes and report connection status."""
    vault_dir = tmp_path / "vault"
    vault_dir.mkdir(parents=True, exist_ok=True)

    provider = VaultLocalNotesProvider(paths=[str(vault_dir)])
    result = provider.test_connection()
    assert result.ok is True

    note_path = provider.create_note(title="Test Note", content="Body")
    assert Path(note_path).exists()
