from typing import Literal from fastapi import APIRouter, Depends, HTTPException, Query from psycopg import AsyncConnection from pydantic import BaseModel, Field from mm_api.db import get_conn from mm_api.dependencies import current_user, verified_user import mm_api.services.issue as svc import mm_api.services.permission as perm_svc router = APIRouter(prefix="/issues", tags=["issues"]) class IssueCreate(BaseModel): title: str = Field(..., min_length=5, max_length=300) body: str = Field(..., min_length=20) category_id: int location_id: int class IssueUpdate(BaseModel): title: str | None = Field(None, min_length=5, max_length=300) body: str | None = Field(None, min_length=20) class StatusUpdate(BaseModel): status: Literal["open", "in_progress", "resolved", "rejected", "duplicate"] class VoteRequest(BaseModel): vote: Literal["resolved", "ongoing"] class CommentCreate(BaseModel): body: str = Field(..., min_length=1, max_length=5000) parent_id: int | None = None @router.get("/categories") async def list_categories( parent_id: int | None = Query(None), conn: AsyncConnection = Depends(get_conn), ): return await svc.list_categories(conn, parent_id) @router.get("/categories/{category_id}") async def get_category( category_id: int, conn: AsyncConnection = Depends(get_conn), ): cat = await svc.get_category(conn, category_id) if not cat: raise HTTPException(404, "Kategori bulunamadı") return cat @router.get("") async def list_issues( location_id: int | None = Query(None), category_id: int | None = Query(None), status: str | None = Query(None), limit: int = Query(20, ge=1, le=100), offset: int = Query(0, ge=0), conn: AsyncConnection = Depends(get_conn), ): return await svc.list_issues(conn, location_id, category_id, status, limit=limit, offset=offset) @router.post("", status_code=201) async def create_issue( data: IssueCreate, user: dict = Depends(verified_user), conn: AsyncConnection = Depends(get_conn), ): if not await perm_svc.can(conn, user["id"], "create", "issue"): raise HTTPException(403, "Sorun bildirme yetkiniz yok") try: return await svc.create_issue(conn, user["id"], data.title, data.body, data.category_id, data.location_id) except ValueError as e: raise HTTPException(400, str(e)) @router.get("/{issue_id}") async def get_issue( issue_id: int, user: dict = Depends(current_user), conn: AsyncConnection = Depends(get_conn), ): issue = await svc.get_issue(conn, issue_id) if not issue: raise HTTPException(404, "Sorun bulunamadı") return issue @router.patch("/{issue_id}") async def update_issue( issue_id: int, data: IssueUpdate, user: dict = Depends(verified_user), conn: AsyncConnection = Depends(get_conn), ): is_admin = await perm_svc.can(conn, user["id"], "update", "issue", is_admin=True) if not is_admin and not await perm_svc.can(conn, user["id"], "update", "issue"): raise HTTPException(403, "Yetersiz yetki") try: return await svc.update_issue(conn, issue_id, user["id"], is_admin, data.title, data.body) except PermissionError as e: raise HTTPException(403, str(e)) except ValueError as e: raise HTTPException(400, str(e)) @router.put("/{issue_id}/status") async def update_status( issue_id: int, data: StatusUpdate, user: dict = Depends(current_user), conn: AsyncConnection = Depends(get_conn), ): if not await perm_svc.can(conn, user["id"], "status", "issue", is_admin=True): raise HTTPException(403, "Yetersiz yetki") try: return await svc.update_status(conn, issue_id, data.status) except ValueError as e: raise HTTPException(400, str(e)) @router.delete("/{issue_id}", status_code=204) async def delete_issue( issue_id: int, user: dict = Depends(current_user), conn: AsyncConnection = Depends(get_conn), ): if not await perm_svc.can(conn, user["id"], "delete", "issue", is_admin=True): raise HTTPException(403, "Yetersiz yetki") try: await svc.delete_issue(conn, issue_id) except ValueError as e: raise HTTPException(400, str(e)) @router.post("/{issue_id}/vote") async def vote( issue_id: int, data: VoteRequest, user: dict = Depends(verified_user), conn: AsyncConnection = Depends(get_conn), ): if not await perm_svc.can(conn, user["id"], "create", "vote"): raise HTTPException(403, "Oy kullanma yetkiniz yok") try: return await svc.cast_vote(conn, issue_id, user["id"], data.vote) except ValueError as e: raise HTTPException(400, str(e)) @router.get("/{issue_id}/comments") async def list_comments( issue_id: int, user: dict = Depends(current_user), conn: AsyncConnection = Depends(get_conn), ): return await svc.list_comments(conn, issue_id) @router.post("/{issue_id}/comments", status_code=201) async def add_comment( issue_id: int, data: CommentCreate, user: dict = Depends(verified_user), conn: AsyncConnection = Depends(get_conn), ): if not await perm_svc.can(conn, user["id"], "create", "comment"): raise HTTPException(403, "Yorum yapma yetkiniz yok") try: return await svc.add_comment(conn, issue_id, user["id"], data.body, data.parent_id) except ValueError as e: raise HTTPException(400, str(e)) @router.delete("/comments/{comment_id}", status_code=204) async def delete_comment( comment_id: int, user: dict = Depends(current_user), conn: AsyncConnection = Depends(get_conn), ): is_admin = await perm_svc.can(conn, user["id"], "delete", "comment", is_admin=True) try: await svc.delete_comment(conn, comment_id, user["id"], is_admin) except PermissionError as e: raise HTTPException(403, str(e)) except ValueError as e: raise HTTPException(404, str(e))