import logging
import time
from typing import Optional

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

from src.api.v1.deps import get_current_active_user_with_set_db
from src.db.models.app_user import AppUser
from src.db.session import get_db
from src.domain.schemas import (
    AddForm3LineBody,
    AddProjectBody,
    BaseListResponse,
    BaseSingleResponse,
    CellPatch,
    CellsPatch,
    ResponseBase,
    SheetResponse,
    UpdProjectBody,
)
from src.services.project_service import ProjectService


logger = logging.getLogger()


router = APIRouter(tags=["forms"])
FORM3_ALLOWED_SECTIONS = {"q1", "q2", "q3", "q4", "year"}


def _parse_sections(sections: Optional[str]) -> Optional[list[str]]:
    if not sections:
        return None
    parsed = [s.strip() for s in sections.split(",") if s.strip()]
    invalid = sorted(set(parsed) - FORM3_ALLOWED_SECTIONS)
    if invalid:
        raise HTTPException(400, f"Недопустимые sections для FORM_3: {', '.join(invalid)}")
    return parsed


def _rows_to_payload(rows: list[tuple]) -> list[dict]:
    return [
        {"row_type": row[0], "depth": row[1], "sort_order": row[2], "data": row[3]}
        for row in rows
    ]


@router.get("/projects")
async def get_projects(
    offset: int = 0,
    limit: int = 100,
    year: Optional[int] = None,
    branch_id: Optional[int] = None,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> BaseListResponse[dict]:
    project_service = ProjectService(db)
    count, projects = await project_service.get_list(
        user=current_user,
        with_count=True,
        year=year,
        branch_id=branch_id,
        offset=offset,
        limit=limit,
    )
    return BaseListResponse(result=projects, count=count)


@router.get("/projects/{project_id}")
async def get_project(
    project_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> BaseSingleResponse[dict]:
    project_service = ProjectService(db)
    project = await project_service.get(project_id=project_id, user=current_user)
    if not project:
        raise HTTPException(404, "Project not found")
    return BaseSingleResponse(result=project)


@router.get("/projects/{project_id}/report/{year}/{report_type}")
async def get_project_report(
    project_id: int,
    year: int,
    report_type: str,
    response: Response,
    sections: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> BaseListResponse[SheetResponse]:
    project_service = ProjectService(db)
    t0 = time.perf_counter()
    rows = await project_service.get_report_rows(
        project_id=project_id,
        year=year,
        report_type=report_type,
        sections=_parse_sections(sections),
        user=current_user,
    )
    db_ms = (time.perf_counter() - t0) * 1000
    response.headers["X-DB-Time-Ms"] = f"{db_ms:.2f}"
    extra={
        "type": "db_time",
        "time_ms": f"{db_ms:.2f}", 
        "handler": "get_project_report",
        "project_id": project_id,
        "year": year,
        "report_type": report_type,
        "sections": sections,
        "user": current_user.id,
    }
    logger.info(
        f"db_time: {extra}", 
        extra=extra,
    )
    return BaseListResponse(count=len(rows), result=_rows_to_payload(rows))


@router.get("/rf-rollup/{branch_id}/{year}")
async def get_rf_rollup(
    branch_id: int,
    year: int,
    response: Response,
    sections: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> BaseListResponse[SheetResponse]:
    project_service = ProjectService(db)
    t0 = time.perf_counter()
    rows = await project_service.get_rf_rollup_rows(
        branch_id=branch_id,
        year=year,
        sections=_parse_sections(sections),
        user=current_user,
    )
    db_ms = (time.perf_counter() - t0) * 1000
    response.headers["X-DB-Time-Ms"] = f"{db_ms:.2f}"
    extra={
        "type": "db_time",
        "time_ms": f"{db_ms:.2f}", 
        "handler": "get_project_report",
        "branch_id": branch_id,
        "year": year,
        "sections": sections,
        "user": current_user.id,
    }
    logger.info(
        f"db_time: {extra}", 
        extra=extra,
    )
    return BaseListResponse(count=len(rows), result=_rows_to_payload(rows))


@router.patch("/projects/{project_id}/report/{year}/{report_type}/cell")
async def upd_form3_cell(
    project_id: int,
    year: int,
    report_type: str,
    body: CellPatch,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> list[SheetResponse]:
    project_service = ProjectService(db)
    rows = await project_service.upd_form3_cell(
        project_id=project_id,
        year=year,
        report_type=report_type,
        line_id=body.line_id,
        column=body.column,
        value=body.value,
        user=current_user,
    )
    return _rows_to_payload(rows)


@router.patch("/projects/{project_id}/report/{year}/{report_type}/cells")
async def upd_form3_cells(
    project_id: int,
    year: int,
    report_type: str,
    body: CellsPatch,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> list[SheetResponse]:
    project_service = ProjectService(db)
    rows = await project_service.upd_form3_cells(
        project_id=project_id,
        year=year,
        report_type=report_type,
        changes=[change.model_dump() for change in body.changes],
        user=current_user,
    )
    return _rows_to_payload(rows)


@router.post("/projects/{project_id}/report/{year}/{report_type}/line")
async def add_form3_line(
    project_id: int,
    year: int,
    report_type: str,
    body: AddForm3LineBody,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> list[SheetResponse]:
    project_service = ProjectService(db)
    rows = await project_service.add_form3_line(
        project_id=project_id,
        year=year,
        report_type=report_type,
        expense_item_id=body.expense_item_id,
        user=current_user,
    )
    return _rows_to_payload(rows)


@router.delete("/projects/{project_id}/report/{year}/{report_type}/line/{line_id}")
async def del_form3_line(
    project_id: int,
    year: int,
    report_type: str,
    line_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> list[SheetResponse]:
    project_service = ProjectService(db)
    rows = await project_service.del_form3_line(
        project_id=project_id,
        year=year,
        report_type=report_type,
        line_id=line_id,
        user=current_user,
    )
    return _rows_to_payload(rows)


@router.patch("/project/{project_id}")
async def upd_project(
    project_id: int,
    body: UpdProjectBody,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> dict:
    project_service = ProjectService(db)
    result = await project_service.upd_project(
        project_id=project_id,
        column=body.column,
        value=body.value,
        user=current_user,
    )
    return result


@router.delete("/projects/{project_id}")
async def delete_project(
    project_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> ResponseBase:
    project_service = ProjectService(db)
    await project_service.delete_project(project_id=project_id, user=current_user)
    return ResponseBase(success=True, message="Проект удалён")


@router.post("/projects")
async def add_project(
    body: AddProjectBody,
    db: AsyncSession = Depends(get_db),
    current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> dict:
    project_service = ProjectService(db)
    project_id, limit_report_id, current_expenses_report_id = await project_service.add_project(
        name=body.name,
        year=body.year,
        org_unit_id=body.branch_id,
        level=body.level,
        parent_id=body.parent_id,
        project_type=body.project_type,
        vsp_format=body.vsp_format,
        placement_type=body.placement_type,
        object_address=body.object_address,
        staff_count=body.staff_count,
        total_area=body.total_area,
    )
    return {
        "project_id": project_id,
        "limit_report_id": limit_report_id,
        "current_expenses_report_id": current_expenses_report_id,
    }