from datetime import date
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.dependencies import CurrentUser, require_role
from app.models.project import Project
from app.models.time_entry import TimeEntry, EntryStatus
from app.models.user import User, UserRole
from app.schemas.project import (
    ProjectCreate,
    ProjectListResponse,
    ProjectOut,
    ProjectTimeReport,
    ProjectUpdate,
)

router = APIRouter(prefix="/projects", tags=["Projekte"])

_manager_roles = (UserRole.MANAGER, UserRole.HR, UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN)


def _assert_company(project: Project, company_id: UUID) -> None:
    if project.company_id != company_id:
        raise HTTPException(404, "Projekt nicht gefunden")


@router.get("", response_model=ProjectListResponse)
async def list_projects(
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
    include_inactive: bool = Query(False),
):
    stmt = select(Project).where(Project.company_id == current_user.company_id)
    if not include_inactive:
        stmt = stmt.where(Project.is_active == True)
    stmt = stmt.order_by(Project.name)
    result = await db.scalars(stmt)
    items = list(result.all())
    return ProjectListResponse(total=len(items), items=items)


@router.post("", response_model=ProjectOut, status_code=201)
async def create_project(
    data: ProjectCreate,
    current_user: User = require_role(*_manager_roles),
    db: AsyncSession = Depends(get_db),
):
    project = Project(
        company_id=current_user.company_id,
        name=data.name,
        description=data.description,
        color=data.color,
        budget_hours=data.budget_hours,
    )
    db.add(project)
    await db.commit()
    return project


@router.get("/report/summary", response_model=list[ProjectTimeReport])
async def projects_summary(
    current_user: User = require_role(*_manager_roles),
    db: AsyncSession = Depends(get_db),
    date_from: date | None = Query(None),
    date_to: date | None = Query(None),
):
    projects = list((await db.scalars(
        select(Project).where(Project.company_id == current_user.company_id, Project.is_active == True)
    )).all())

    result = []
    for project in projects:
        stmt = (
            select(TimeEntry)
            .join(TimeEntry.user)
            .where(
                TimeEntry.project_id == project.id,
                User.company_id == current_user.company_id,
                TimeEntry.status == EntryStatus.APPROVED,
                TimeEntry.end_time.is_not(None),
            )
        )
        if date_from:
            stmt = stmt.where(TimeEntry.date >= date_from)
        if date_to:
            stmt = stmt.where(TimeEntry.date <= date_to)

        entries = list((await db.scalars(stmt)).all())
        total_minutes = sum(e.worked_minutes or 0 for e in entries)
        total_hours = round(total_minutes / 60, 2)

        budget_used_pct = None
        if project.budget_hours and float(project.budget_hours) > 0:
            budget_used_pct = round(total_hours / float(project.budget_hours) * 100, 1)

        result.append(ProjectTimeReport(
            project_id=project.id,
            project_name=project.name,
            project_color=project.color,
            total_hours=total_hours,
            entry_count=len(entries),
            budget_hours=float(project.budget_hours) if project.budget_hours else None,
            budget_used_pct=budget_used_pct,
        ))

    result.sort(key=lambda x: x.total_hours, reverse=True)
    return result


@router.get("/{project_id}", response_model=ProjectOut)
async def get_project(
    project_id: UUID,
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
):
    project = await db.get(Project, project_id)
    if not project:
        raise HTTPException(404, "Projekt nicht gefunden")
    _assert_company(project, current_user.company_id)
    return project


@router.patch("/{project_id}", response_model=ProjectOut)
async def update_project(
    project_id: UUID,
    data: ProjectUpdate,
    current_user: User = require_role(*_manager_roles),
    db: AsyncSession = Depends(get_db),
):
    project = await db.get(Project, project_id)
    if not project:
        raise HTTPException(404, "Projekt nicht gefunden")
    _assert_company(project, current_user.company_id)

    for field, value in data.model_dump(exclude_unset=True).items():
        setattr(project, field, value)

    await db.commit()
    return project


@router.delete("/{project_id}", status_code=204)
async def delete_project(
    project_id: UUID,
    current_user: User = require_role(*_manager_roles),
    db: AsyncSession = Depends(get_db),
):
    project = await db.get(Project, project_id)
    if not project:
        raise HTTPException(404, "Projekt nicht gefunden")
    _assert_company(project, current_user.company_id)
    project.is_active = False
    await db.commit()


@router.get("/{project_id}/report", response_model=ProjectTimeReport)
async def project_time_report(
    project_id: UUID,
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
    date_from: date | None = Query(None),
    date_to: date | None = Query(None),
):
    project = await db.get(Project, project_id)
    if not project:
        raise HTTPException(404, "Projekt nicht gefunden")
    _assert_company(project, current_user.company_id)

    stmt = (
        select(TimeEntry)
        .join(TimeEntry.user)
        .where(
            TimeEntry.project_id == project_id,
            User.company_id == current_user.company_id,
            TimeEntry.status == EntryStatus.APPROVED,
            TimeEntry.end_time.is_not(None),
        )
    )
    if date_from:
        stmt = stmt.where(TimeEntry.date >= date_from)
    if date_to:
        stmt = stmt.where(TimeEntry.date <= date_to)

    entries = list((await db.scalars(stmt)).all())
    total_minutes = sum(e.worked_minutes or 0 for e in entries)
    total_hours = round(total_minutes / 60, 2)

    budget_used_pct = None
    if project.budget_hours and float(project.budget_hours) > 0:
        budget_used_pct = round(total_hours / float(project.budget_hours) * 100, 1)

    return ProjectTimeReport(
        project_id=project.id,
        project_name=project.name,
        project_color=project.color,
        total_hours=total_hours,
        entry_count=len(entries),
        budget_hours=float(project.budget_hours) if project.budget_hours else None,
        budget_used_pct=budget_used_pct,
    )
