"""GitHub Issues provider for tasks and notes operations."""

from __future__ import annotations

from typing import Any

import requests
from requests.exceptions import RequestException
from sqlalchemy.orm import Session

from app.services.integrations.errors import ProviderConnectionError, ProviderNotConfiguredError
from app.services.integrations.types import NotesHit, ProviderConnectionResult, UnifiedTask

REQUEST_TIMEOUT_SECONDS = 20
GITHUB_API_BASE = "https://api.github.com"


class GitHubIssuesProvider:
    """Token-based GitHub Issues adapter."""

    provider_id = "github_issues"
    display_name = "GitHub Issues"

    def __init__(self, *, token: str, repo: str):
        self._token = (token or "").strip()
        self._repo = (repo or "").strip()
        if not self._token:
            raise ProviderNotConfiguredError("GitHub token is required")
        if not self._repo or "/" not in self._repo:
            raise ProviderNotConfiguredError("GitHub repo must be in owner/repo format")

    def _headers(self) -> dict[str, str]:
        return {
            "Authorization": f"Bearer {self._token}",
            "Accept": "application/vnd.github+json",
            "X-GitHub-Api-Version": "2022-11-28",
        }

    def _request(self, method: str, path: str, *, json_payload: dict[str, Any] | None = None) -> dict[str, Any] | list[Any]:
        url = f"{GITHUB_API_BASE}{path}"
        try:
            response = requests.request(
                method=method,
                url=url,
                headers=self._headers(),
                json=json_payload,
                timeout=REQUEST_TIMEOUT_SECONDS,
            )
        except RequestException as exc:
            raise ProviderConnectionError(f"GitHub request failed: {exc}") from exc

        if response.status_code >= 400:
            detail = response.text.strip() or "GitHub API request failed"
            raise ProviderConnectionError(f"GitHub API error ({response.status_code}): {detail}")
        try:
            return response.json()
        except ValueError as exc:
            raise ProviderConnectionError("GitHub API returned invalid JSON") from exc

    def _issue_to_task(self, issue: dict[str, Any]) -> UnifiedTask:
        due = issue.get("milestone") if isinstance(issue.get("milestone"), dict) else {}
        due_date = str(due.get("due_on")).strip() if due.get("due_on") else None
        body = str(issue.get("body", "")).strip()
        state = str(issue.get("state", "")).strip().lower()
        return UnifiedTask(
            provider=self.provider_id,
            id=str(issue.get("number", "")),
            title=str(issue.get("title", "")).strip() or "Untitled issue",
            description=body or None,
            due_date=due_date,
            due_string=due_date,
            completed=state == "closed",
            raw={
                "url": issue.get("html_url"),
                "state": issue.get("state"),
            },
        )

    def list_tasks(self, *, include_completed: bool = False) -> list[UnifiedTask]:
        state = "all" if include_completed else "open"
        payload = self._request("GET", f"/repos/{self._repo}/issues?state={state}&per_page=100")
        if not isinstance(payload, list):
            return []
        tasks = [self._issue_to_task(item) for item in payload if isinstance(item, dict) and "pull_request" not in item]
        if include_completed:
            return tasks
        return [item for item in tasks if not item.completed]

    def create_task(self, *, content: str, description: str | None = None, due_string: str | None = None) -> UnifiedTask:
        payload = {
            "title": content,
            "body": description or "",
        }
        issue = self._request("POST", f"/repos/{self._repo}/issues", json_payload=payload)
        if not isinstance(issue, dict):
            raise ProviderConnectionError("Unexpected GitHub issue response")
        return self._issue_to_task(issue)

    def complete_task(self, *, task_id: str) -> None:
        _ = self._request(
            "PATCH",
            f"/repos/{self._repo}/issues/{task_id}",
            json_payload={"state": "closed"},
        )

    def update_due(self, *, task_id: str, due_string: str) -> UnifiedTask | None:
        # GitHub due updates require milestone workflow; not implemented in minimal pass.
        return None

    def search(self, *, query: str, limit: int = 5, db: Session | None = None) -> list[NotesHit]:
        q = f"{query} repo:{self._repo} is:issue"
        payload = self._request("GET", f"/search/issues?q={requests.utils.quote(q)}&per_page={max(1, min(limit, 20))}")
        items = payload.get("items") if isinstance(payload, dict) else None
        if not isinstance(items, list):
            return []

        hits: list[NotesHit] = []
        for item in items:
            if not isinstance(item, dict):
                continue
            snippet = str(item.get("body", "")).strip()
            if len(snippet) > 260:
                snippet = f"{snippet[:257]}..."
            hits.append(
                NotesHit(
                    provider=self.provider_id,
                    id=str(item.get("number", "")),
                    title=str(item.get("title", "")).strip() or "Issue",
                    snippet=snippet,
                    source=str(item.get("html_url", "")),
                    raw={"state": item.get("state")},
                ),
            )
        return hits

    def create_note(self, *, title: str, content: str, db: Session | None = None) -> str:
        issue = self._request(
            "POST",
            f"/repos/{self._repo}/issues",
            json_payload={"title": title, "body": content},
        )
        if not isinstance(issue, dict):
            raise ProviderConnectionError("Unexpected GitHub issue response")
        return str(issue.get("number", ""))

    def append_note(self, *, target_id: str, content: str, db: Session | None = None) -> None:
        _ = self._request(
            "POST",
            f"/repos/{self._repo}/issues/{target_id}/comments",
            json_payload={"body": content},
        )

    def test_connection(self, db: Session | None = None) -> ProviderConnectionResult:
        try:
            payload = self._request("GET", f"/repos/{self._repo}")
        except Exception as exc:
            return ProviderConnectionResult(ok=False, detail=str(exc))
        if isinstance(payload, dict) and payload.get("full_name"):
            return ProviderConnectionResult(ok=True, detail=f"Connected to {payload['full_name']}")
        return ProviderConnectionResult(ok=False, detail="GitHub test returned unexpected payload")
