"""Parse Gitea webhook payloads into ServiceEvent objects."""

from __future__ import annotations

import logging
from datetime import datetime, timezone
from typing import Any

from notify_bridge_core.models.events import EventType, ServiceEvent
from notify_bridge_core.providers.base import ServiceProviderType

from .models import (
    GiteaComment,
    GiteaCommit,
    GiteaIssue,
    GiteaPullRequest,
    GiteaRelease,
    GiteaRepository,
    GiteaUser,
)

_LOGGER = logging.getLogger(__name__)

# Map Gitea X-Gitea-Event header values to our event types.
# For issues/PRs, the action field refines the mapping further.
_GITEA_EVENT_MAP: dict[str, EventType | None] = {
    "push": EventType.PUSH,
    "issues": None,  # refined by action
    "issue_comment": None,  # refined by action + is_pull
    "pull_request": None,  # refined by action
    "release": EventType.RELEASE_PUBLISHED,
}

_ISSUE_ACTION_MAP: dict[str, EventType] = {
    "opened": EventType.ISSUE_OPENED,
    "closed": EventType.ISSUE_CLOSED,
}

_PR_ACTION_MAP: dict[str, EventType] = {
    "opened": EventType.PR_OPENED,
    "closed": EventType.PR_CLOSED,
    # "closed" + merged flag → PR_MERGED, handled specially
}


def parse_webhook(
    event_header: str,
    payload: dict[str, Any],
    provider_name: str,
) -> ServiceEvent | None:
    """Parse a Gitea webhook payload into a ServiceEvent.

    Args:
        event_header: Value of the X-Gitea-Event header.
        payload: Parsed JSON body of the webhook.
        provider_name: Display name of the ServiceProvider instance.

    Returns:
        A ServiceEvent, or None if the event/action is not tracked.
    """
    if event_header not in _GITEA_EVENT_MAP:
        _LOGGER.debug("Ignoring untracked Gitea event header: %s", event_header)
        return None

    repo_data = payload.get("repository", {})
    repo = GiteaRepository.from_payload(repo_data)
    sender = GiteaUser.from_payload(payload.get("sender", {}))
    action = payload.get("action", "")

    event_type = _resolve_event_type(event_header, action, payload)
    if event_type is None:
        _LOGGER.debug(
            "Ignoring Gitea event %s with action=%s (not mapped)", event_header, action
        )
        return None

    extra = _build_extra(event_header, event_type, payload, repo, sender)

    return ServiceEvent(
        event_type=event_type,
        provider_type=ServiceProviderType.GITEA,
        provider_name=provider_name,
        collection_id=repo.full_name,
        collection_name=repo.full_name,
        timestamp=datetime.now(timezone.utc),
        extra=extra,
    )


def _resolve_event_type(
    event_header: str, action: str, payload: dict[str, Any]
) -> EventType | None:
    """Determine the EventType from header + action."""
    direct = _GITEA_EVENT_MAP.get(event_header)
    if direct is not None:
        # Release: only "published" action
        if event_header == "release" and action != "published":
            return None
        return direct

    if event_header == "issues":
        return _ISSUE_ACTION_MAP.get(action)

    if event_header == "pull_request":
        pr_data = payload.get("pull_request", {})
        if action == "closed" and pr_data.get("merged", False):
            return EventType.PR_MERGED
        return _PR_ACTION_MAP.get(action)

    if event_header == "issue_comment":
        # Gitea sends issue_comment for both issue and PR comments.
        is_pull = payload.get("is_pull", False)
        if action == "created":
            return EventType.PR_COMMENTED if is_pull else EventType.ISSUE_COMMENTED
        return None

    return None


def _build_extra(
    event_header: str,
    event_type: EventType,
    payload: dict[str, Any],
    repo: GiteaRepository,
    sender: GiteaUser,
) -> dict[str, Any]:
    """Build the provider-specific extra dict for template rendering."""
    extra: dict[str, Any] = {
        "sender": sender.login,
        "sender_name": sender.full_name or sender.login,
        "sender_avatar": sender.avatar_url,
        "repo_name": repo.name,
        "repo_full_name": repo.full_name,
        "repo_url": repo.html_url,
        "repo_description": repo.description,
    }

    if event_type == EventType.PUSH:
        _enrich_push(extra, payload)
    elif event_type in (EventType.ISSUE_OPENED, EventType.ISSUE_CLOSED):
        _enrich_issue(extra, payload)
    elif event_type == EventType.ISSUE_COMMENTED:
        _enrich_issue(extra, payload)
        _enrich_comment(extra, payload)
    elif event_type in (EventType.PR_OPENED, EventType.PR_CLOSED, EventType.PR_MERGED):
        _enrich_pr(extra, payload)
    elif event_type == EventType.PR_COMMENTED:
        _enrich_pr(extra, payload)
        _enrich_comment(extra, payload)
    elif event_type == EventType.RELEASE_PUBLISHED:
        _enrich_release(extra, payload)

    return extra


def _enrich_push(extra: dict[str, Any], payload: dict[str, Any]) -> None:
    ref = payload.get("ref", "")
    extra["ref"] = ref
    extra["branch"] = ref.removeprefix("refs/heads/")
    extra["before"] = payload.get("before", "")
    extra["after"] = payload.get("after", "")
    extra["compare_url"] = payload.get("compare_url", "")

    raw_commits = payload.get("commits", [])
    commits = [GiteaCommit.from_payload(c) for c in raw_commits]
    extra["commits"] = [
        {
            "id": c.id,
            "short_id": c.id[:7],
            "message": c.message,
            "url": c.url,
            "author": c.author_name,
        }
        for c in commits
    ]
    extra["commit_count"] = len(commits)


def _enrich_issue(extra: dict[str, Any], payload: dict[str, Any]) -> None:
    issue_data = payload.get("issue", {})
    issue = GiteaIssue.from_payload(issue_data)
    extra["issue_number"] = issue.number
    extra["issue_title"] = issue.title
    extra["issue_url"] = issue.html_url
    extra["issue_state"] = issue.state
    extra["issue_body"] = issue.body
    extra["issue_labels"] = issue.labels


def _enrich_pr(extra: dict[str, Any], payload: dict[str, Any]) -> None:
    pr_data = payload.get("pull_request", {})
    pr = GiteaPullRequest.from_payload(pr_data)
    extra["pr_number"] = pr.number
    extra["pr_title"] = pr.title
    extra["pr_url"] = pr.html_url
    extra["pr_state"] = pr.state
    extra["pr_body"] = pr.body
    extra["pr_merged"] = pr.merged
    extra["pr_base"] = pr.base_branch
    extra["pr_head"] = pr.head_branch
    extra["pr_labels"] = pr.labels


def _enrich_comment(extra: dict[str, Any], payload: dict[str, Any]) -> None:
    comment_data = payload.get("comment", {})
    comment = GiteaComment.from_payload(comment_data)
    extra["comment_body"] = comment.body
    extra["comment_url"] = comment.html_url
    if comment.user:
        extra["comment_author"] = comment.user.login


def _enrich_release(extra: dict[str, Any], payload: dict[str, Any]) -> None:
    release_data = payload.get("release", {})
    release = GiteaRelease.from_payload(release_data)
    extra["release_tag"] = release.tag_name
    extra["release_name"] = release.name
    extra["release_url"] = release.html_url
    extra["release_body"] = release.body
    extra["release_draft"] = release.draft
    extra["release_prerelease"] = release.prerelease
