"""Dispatch GitLab user mirror webhooks and keep mirror targets in sync."""
from __future__ import annotations

from typing import Any, Optional

from sqlalchemy.ext.asyncio import AsyncSession

from src.domain.gitlab_user_mirror.repository import ProjectUserGitlabMirrorRepository


async def dispatch(db: AsyncSession, event: str, payload: dict[str, Any]) -> dict[str, Any]:
    repo = ProjectUserGitlabMirrorRepository(db)
    event_name = str(payload.get("event_name") or payload.get("object_kind") or "").strip()
    normalized = event_name or event

    current_path = _current_project_path(payload)
    old_path = _old_project_path(payload)

    if normalized in {"project_destroy", "project_destroyed", "project_delete"}:
        if not current_path:
            return {"action": "ignored", "reason": "no_project_path", "event": normalized}
        count = await repo.disable_by_target_path(
            current_path,
            "GitLab project was deleted.",
        )
        return {"action": "project_deleted", "path": current_path, "mirrors_disabled": count}

    if normalized in {"project_rename", "project_transfer", "project_update"}:
        if old_path and current_path and old_path != current_path:
            count = await repo.rename_target_path(old_path, current_path)
            return {
                "action": "project_path_updated",
                "old_path": old_path,
                "new_path": current_path,
                "mirrors_updated": count,
            }
        return {
            "action": "project_update_noted",
            "path": current_path,
            "event": normalized,
        }

    if normalized in {"push", "tag_push", "repository_update"} or event == "Push Hook":
        if not current_path:
            return {"action": "push_ignored", "reason": "no_project_path"}
        mirrors = await repo.list_by_target_path(current_path)
        return {"action": "push_noted", "path": current_path, "mirror_count": len(mirrors)}

    return {"action": "ignored", "event": event, "event_name": normalized}


def _current_project_path(payload: dict[str, Any]) -> Optional[str]:
    project = payload.get("project") or {}
    raw = (
        payload.get("path_with_namespace")
        or payload.get("full_path")
        or project.get("path_with_namespace")
        or project.get("path_with_namespace")
    )
    if isinstance(raw, str) and raw.strip():
        return raw.strip()
    return None


def _old_project_path(payload: dict[str, Any]) -> Optional[str]:
    changes = payload.get("changes") or {}
    for key in ("path_with_namespace", "full_path", "path"):
        value = changes.get(key)
        if isinstance(value, dict):
            old = value.get("previous") or value.get("old")
            if isinstance(old, str) and old.strip():
                return old.strip()
    raw = payload.get("old_path_with_namespace") or payload.get("old_full_path")
    if isinstance(raw, str) and raw.strip():
        return raw.strip()
    return None
