"""Merge request create / fast-forward HTTP endpoints and tag view response."""

from __future__ import annotations

from typing import Annotated
from urllib.parse import quote, urlencode

import pygit2
from fastapi import APIRouter, Depends, Form, HTTPException, Query
from fastapi.responses import HTMLResponse, RedirectResponse
from pygittools.merge import (
    MergeRequest,
    merge_request_status_fields,
    resolve_merge_request_tag_to_tip,
)
from pygittools.validation import is_valid_ref_format

from pygitweb.auth import access_token_from_request, principal_from_session, require_permission
from pygitweb.dependencies import ValidatedReadableFormProject, ValidatedReadableQueryProject
from pygitweb.git_helpers import open_repo, resolve_mr_opener
from pygitweb.permissions import Permission
from pygitweb.templates_env import render_page


def _branch_short_name(ref: str) -> str:
    return ref.removeprefix("refs/heads/") if ref.startswith("refs/heads/") else ref


merge_router = APIRouter(tags=["merge_requests"])


def _head_view_url(project: str, repo: pygit2.Repository, head_hex: str) -> tuple[str, str]:
    try:
        oid = pygit2.Oid(hex=head_hex)
        obj = repo[oid]
    except (KeyError, ValueError, TypeError):
        return head_hex[:7], ""
    t = getattr(obj, "type_str", "") or ""
    short = head_hex[:7]
    if t == "commit":
        return short, f"/project/{project}?a=commit&h={quote(head_hex, safe='')}"
    if t == "tag":
        return short, f"/project/{project}?a=tag&h={quote(head_hex, safe='')}"
    return short, ""


def merge_request_tag_response(
    project: str,
    repo: pygit2.Repository,
    mr: MergeRequest,
    tag_name: str,
    tag_oid: str,
    tagger: str,
    tagger_date: str,
    notice: str | None = None,
) -> HTMLResponse | RedirectResponse:
    new_oid = mr.refresh_from_theirs(repo)
    if new_oid is not None:
        return RedirectResponse(
            url=f"/project/{project}?a=tag&h={quote(str(new_oid), safe='')}",
            status_code=302,
        )

    status_fields = merge_request_status_fields(repo, mr)
    head_short, head_url = _head_view_url(project, repo, str(mr.target))
    ours_ref = mr.ours
    ours_url = f"/project/{project}?a=log&h={quote(ours_ref, safe='')}"
    theirs_ref = mr.theirs or ""
    theirs_url = f"/project/{project}?a=log&h={quote(theirs_ref, safe='')}" if mr.theirs else ""

    comments_rows: list[tuple[str, str]] = []
    for c in mr.comments:
        ch = str(c)
        comments_rows.append((ch[:7], f"/project/{project}?a=tag&h={quote(ch, safe='')}"))

    current_tag_oid = str(repo.references[mr.name].resolve().target)

    ctx = {
        "project": project,
        "project_url": f"/project/{project}",
        "tag_name": tag_name,
        "tag_oid": current_tag_oid,
        "tag_view_url": f"/project/{project}?a=tag&h={quote(current_tag_oid, safe='')}",
        "mr_title": mr.title,
        "mr_description": mr.description or "",
        "ours_ref": ours_ref,
        "ours_url": ours_url,
        "theirs_ref": theirs_ref,
        "theirs_url": theirs_url,
        "head_short": head_short,
        "head_url": head_url,
        "head_full": str(mr.target),
        "tagger": tagger,
        "tagger_date": tagger_date,
        "opener": str(mr.opener),
        "created_at": mr.created_at.isoformat() if mr.created_at else "",
        "updated_at": mr.updated_at.isoformat() if mr.updated_at else "",
        "comments": comments_rows,
        "merge_action_url": f"/mr/ff?project={quote(project, safe='')}&h={quote(current_tag_oid, safe='')}",
        "notice": notice or "",
        **status_fields,
    }
    title = f"Merge request: {mr.title or ''} — {project or ''}"
    return render_page("merge_request_tag.html", title=title, **ctx)


@merge_router.post(
    "/create",
    dependencies=[Depends(require_permission(Permission.MR_CREATE, project_from_form=True))],
)
def merge_request_create(
    project: ValidatedReadableFormProject,
    theirs: str = Form(...),
    ours: str = Form(...),
    title: str = Form(""),
    token: Annotated[str | None, Depends(access_token_from_request)] = None,
) -> RedirectResponse:
    theirs_ref = theirs.strip()
    ours_ref = ours.strip()
    if not theirs_ref or not ours_ref:
        raise HTTPException(status_code=400, detail="theirs and ours are required")
    if not is_valid_ref_format(theirs_ref) or not is_valid_ref_format(ours_ref):
        raise HTTPException(status_code=400, detail="Invalid ref format")
    repo = open_repo(project)
    try:
        theirs_tip = repo.revparse_single(theirs_ref).peel(pygit2.Commit).id
    except (KeyError, ValueError, pygit2.GitError) as exc:
        raise HTTPException(status_code=404, detail=f"Cannot resolve theirs ref {theirs_ref!r}") from exc
    try:
        repo.revparse_single(ours_ref)
    except (KeyError, ValueError, pygit2.GitError) as exc:
        raise HTTPException(status_code=404, detail=f"Cannot resolve ours ref {ours_ref!r}") from exc
    opener, opener_warning = resolve_mr_opener(repo, principal_from_session(token))
    t = title.strip()
    if not t:
        t = f"Merge {_branch_short_name(theirs_ref)} into {_branch_short_name(ours_ref)}"
    mr = MergeRequest(
        target=theirs_tip,
        opener=opener,
        ours=ours_ref,
        theirs=theirs_ref,
        title=t,
        description="",
    )
    oid = mr.write(repo)
    params: dict[str, str] = {"a": "tag", "h": str(oid)}
    if opener_warning:
        params["notice"] = opener_warning
    dest = f"/project/{project}?{urlencode(params)}"
    return RedirectResponse(url=dest, status_code=303)


@merge_router.post(
    "/ff",
    dependencies=[Depends(require_permission(Permission.MR_MERGE, project_from_query=True))],
)
def merge_fast_forward(
    project: ValidatedReadableQueryProject,
    h: str = Query(..., description="Annotated tag object id (hex)"),
) -> RedirectResponse:
    if not h or not is_valid_ref_format(h):
        raise HTTPException(status_code=400, detail="Invalid tag hash (h)")
    repo = open_repo(project)
    try:
        obj = repo.revparse_single(h)
    except (KeyError, pygit2.GitError, OSError) as exc:
        raise HTTPException(status_code=404, detail="Tag not found") from exc
    if not isinstance(obj, pygit2.Tag):
        raise HTTPException(status_code=400, detail="Not an annotated tag")
    resolved = resolve_merge_request_tag_to_tip(repo, obj)
    if resolved is None:
        raise HTTPException(status_code=400, detail="Not a merge request tag")
    obj, mr = resolved
    mr.refresh_from_theirs(repo)
    try:
        mr.merge(repo)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    tag_oid = str(repo.references[mr.name].resolve().target)
    dest = f"/project/{project}?a=tag&h={quote(tag_oid, safe='')}"
    return RedirectResponse(url=dest, status_code=303)