from datetime import datetime

from fastapi import APIRouter, Depends, Form, HTTPException, Request
from fastapi.responses import JSONResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session

from ..auth import get_current_user, get_current_user_optional, get_db
from ..repositories import ProjectMemberRepository, ProjectRepository, TaskRepository, TaskRequestRepository, UserRepository
from ..services import TaskRequestService, TaskService, send_overdue_alerts, task_progress_metrics

router = APIRouter()

templates = Jinja2Templates(directory="app/templates")


@router.get("/")
def root(request: Request, db: Session = Depends(get_db)):
    user = get_current_user_optional(request, db)
    if not user:
        return RedirectResponse("/login", status_code=302)
    return RedirectResponse("/projects", status_code=302)


@router.get("/projects")
def list_projects(request: Request, db: Session = Depends(get_db)):
    user = get_current_user(request, db)
    projects_repo = ProjectRepository(db)
    if user.role == "admin":
        projects = projects_repo.list_all()
    else:
        projects = projects_repo.list_for_user(user.id)
    password_status = request.query_params.get("password_status")
    password_error = request.query_params.get("password_error")
    password_messages = {
        "updated": "Contrasena actualizada correctamente.",
        "current_password": "La contrasena actual no es valida.",
        "empty_password": "La nueva contrasena no puede estar vacia.",
        "password_mismatch": "La confirmacion de la contrasena no coincide.",
        "unknown": "No fue posible actualizar la contrasena.",
    }
    profile_status = request.query_params.get("profile_status")
    profile_error = request.query_params.get("profile_error")
    profile_messages = {
        "updated": "Foto de perfil actualizada correctamente.",
        "invalid_photo_format": "La foto debe ser JPG, PNG, GIF o WEBP.",
        "empty_photo": "No se recibio ningun archivo de imagen.",
        "unknown": "No fue posible actualizar la foto de perfil.",
    }
    return templates.TemplateResponse(
        "projects.html",
        {
            "request": request,
            "user": user,
            "projects": projects,
            "password_success": password_messages.get(password_status),
            "password_error": password_messages.get(password_error),
            "profile_success": profile_messages.get(profile_status),
            "profile_error": profile_messages.get(profile_error),
        },
    )


@router.get("/projects/{project_id}")
def project_board(project_id: int, request: Request, db: Session = Depends(get_db)):
    user = get_current_user(request, db)
    project = ProjectRepository(db).get(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="project not found")

    tasks = TaskRepository(db).list_by_project(project_id)
    member_repo = ProjectMemberRepository(db)
    member = member_repo.get_member(project_id, user.id)
    is_owner = member and member.role == "owner"
    if user.role != "admin":
        is_member = member is not None
        has_task = any(task.assigned_user_id == user.id for task in tasks)
        if not is_member and not has_task:
            raise HTTPException(status_code=403, detail="not a member")
    users = UserRepository(db).list_all()
    pending_requests = TaskRequestRepository(db).list_pending_for_user(user.id)
    is_project_admin = user.role == "admin" or is_owner
    now = datetime.utcnow()
    task_metrics = {}
    overdue_notifications = []
    for task in tasks:
        if task.status == "in_progress":
            metrics = task_progress_metrics(task, now)
            task_metrics[task.id] = metrics
        else:
            metrics = None
        if metrics and metrics["overdue_days"] > 0:
            is_responsible = task.assigned_user_id == user.id
            if is_responsible or is_project_admin:
                overdue_notifications.append(
                    {
                        "task": task,
                        "metrics": metrics,
                    }
                )

    send_overdue_alerts(db, tasks, now)

    return templates.TemplateResponse(
        "board.html",
        {
            "request": request,
            "user": user,
            "project": project,
            "tasks": tasks,
            "users": users,
            "pending_requests": pending_requests,
            "overdue_notifications": overdue_notifications,
            "task_metrics": task_metrics,
        },
    )


@router.post("/projects/{project_id}/tasks")
def create_project_task(
    project_id: int,
    request: Request,
    title: str = Form(...),
    description: str = Form(""),
    priority: str = Form("medium"),
    duration_days: int = Form(...),
    assigned_user_id: str = Form(""),
    db: Session = Depends(get_db),
):
    user = get_current_user(request, db)
    project = ProjectRepository(db).get(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="project not found")

    member_repo = ProjectMemberRepository(db)
    if user.role != "admin":
        is_member = member_repo.is_member(project_id, user.id)
        if not is_member:
            tasks = TaskRepository(db).list_by_project(project_id)
            has_task = any(task.assigned_user_id == user.id for task in tasks)
            if not has_task:
                raise HTTPException(status_code=403, detail="not a member")
            member_repo.add_member(project_id, user.id)

    if priority not in {"low", "medium", "high"}:
        raise HTTPException(status_code=400, detail="invalid priority")
    if duration_days < 1:
        raise HTTPException(status_code=400, detail="invalid duration")

    assigned_id = int(assigned_user_id) if assigned_user_id else None
    if user.role != "admin" and assigned_id and assigned_id != user.id:
        raise HTTPException(status_code=403, detail="cannot assign to other users")

    service = TaskService(db)
    service.create_task(
        project_id,
        title,
        description or None,
        priority,
        duration_days,
        user.id,
        assigned_id,
    )
    return RedirectResponse(f"/projects/{project_id}", status_code=302)


@router.post("/tasks/{task_id}/status")
async def update_task_status(
    task_id: int,
    request: Request,
    db: Session = Depends(get_db),
):
    user = get_current_user(request, db)
    try:
        data = await request.json()
    except Exception:
        data = {}
    status = data.get("status")
    if status not in {"not_started", "in_progress", "done"}:
        raise HTTPException(status_code=400, detail="invalid status")

    repo = TaskRepository(db)
    task = repo.get(task_id)
    if not task:
        raise HTTPException(status_code=404, detail="task not found")

    service = TaskService(db)
    try:
        service.update_status(task, status, user)
    except PermissionError:
        raise HTTPException(status_code=403, detail="not allowed")

    return JSONResponse({"ok": True, "status": status})


@router.post("/tasks/{task_id}/delete")
def delete_task(task_id: int, request: Request, db: Session = Depends(get_db)):
    user = get_current_user(request, db)
    repo = TaskRepository(db)
    task = repo.get(task_id)
    if not task:
        raise HTTPException(status_code=404, detail="task not found")

    service = TaskService(db)
    try:
        service.delete_task(task, user)
    except ValueError:
        raise HTTPException(status_code=409, detail="task already started")
    except PermissionError:
        raise HTTPException(status_code=403, detail="not allowed")

    return RedirectResponse(request.headers.get("referer", "/projects"), status_code=302)


@router.post("/tasks/{task_id}/support")
def update_task_support(
    task_id: int,
    request: Request,
    support_url: str = Form(""),
    db: Session = Depends(get_db),
):
    user = get_current_user(request, db)
    repo = TaskRepository(db)
    task = repo.get(task_id)
    if not task:
        raise HTTPException(status_code=404, detail="task not found")
    service = TaskService(db)
    try:
        service.update_support_url(task, support_url.strip(), user)
    except PermissionError:
        raise HTTPException(status_code=403, detail="not allowed")
    except ValueError:
        raise HTTPException(status_code=409, detail="task not finished")
    return RedirectResponse(request.headers.get("referer", "/projects"), status_code=302)


@router.post("/requests")
def create_request(
    request: Request,
    task_id: int = Form(...),
    target_user_id: int = Form(...),
    project_id: int = Form(...),
    db: Session = Depends(get_db),
):
    user = get_current_user(request, db)
    service = TaskRequestService(db)
    try:
        service.create_request(task_id, user.id, target_user_id)
    except ValueError:
        return RedirectResponse(f"/projects/{project_id}", status_code=302)
    return RedirectResponse(f"/projects/{project_id}", status_code=302)


@router.post("/requests/{request_id}/accept")
def accept_request(request_id: int, request: Request, db: Session = Depends(get_db)):
    user = get_current_user(request, db)
    repo = TaskRequestRepository(db)
    req = repo.get(request_id)
    if not req:
        raise HTTPException(status_code=404, detail="request not found")
    service = TaskRequestService(db)
    try:
        service.respond(req, True, user.id)
    except PermissionError:
        raise HTTPException(status_code=403, detail="not allowed")
    return RedirectResponse(request.headers.get("referer", "/projects"), status_code=302)


@router.post("/requests/{request_id}/reject")
def reject_request(request_id: int, request: Request, db: Session = Depends(get_db)):
    user = get_current_user(request, db)
    repo = TaskRequestRepository(db)
    req = repo.get(request_id)
    if not req:
        raise HTTPException(status_code=404, detail="request not found")
    service = TaskRequestService(db)
    try:
        service.respond(req, False, user.id)
    except PermissionError:
        raise HTTPException(status_code=403, detail="not allowed")
    return RedirectResponse(request.headers.get("referer", "/projects"), status_code=302)
