from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from app.api import deps
from app.api.permissions import require_platform_admin
from app.crud.role import role as crud_role
from app.models.user import User
from app.schemas.role import (
    ResourceInfo,
    RoleCreate,
    RolePermissionResponse,
    RolePermissionsUpdate,
    RoleResponse,
    RoleUpdate,
)
from app.services.access.permission_service import PermissionService
from app.services.access.permission_template_service import PermissionTemplateService
from app.services.access.role_service import RoleService
from common_logging import get_logger

router = APIRouter()
logger = get_logger(__name__)


@router.get("/", response_model=list[RoleResponse])
def get_roles(
    skip: int = 0,
    limit: int = 100,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    roles = crud_role.get_multi(db, skip=skip, limit=limit)
    return roles


@router.post("/", response_model=RoleResponse, status_code=status.HTTP_201_CREATED)
def create_role(
    role_in: RoleCreate,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(require_platform_admin()),
):
    existing = crud_role.get_by_code(db, code=role_in.code)
    if existing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="Role with this code already exists"
        )
    role = crud_role.create(db, obj_in=role_in, created_by=current_user.id)
    logger.bind(user_id=current_user.id, role_id=role.id).info("Role created")
    return role


@router.get("/{role_id}", response_model=RoleResponse)
def get_role(
    role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    return role


@router.put("/{role_id}", response_model=RoleResponse)
def update_role(
    role_id: int,
    role_in: RoleUpdate,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(require_platform_admin()),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    if role.is_system:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="System roles cannot be modified"
        )
    role = crud_role.update(db, db_obj=role, obj_in=role_in)
    logger.bind(user_id=current_user.id, role_id=role_id).info("Role updated")
    return role


@router.delete("/{role_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_role(
    role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(require_platform_admin()),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    if role.is_system:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="Cannot delete system role"
        )
    crud_role.remove(db, id=role_id)
    logger.bind(user_id=current_user.id, role_id=role_id).info("Role deleted")
    return None


@router.get("/{role_id}/menus", response_model=list[int])
def get_role_menus(
    role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    menu_ids = crud_role.get_menus(db, role_id=role_id)
    return menu_ids


@router.put("/{role_id}/menus", response_model=dict)
def update_role_menus(
    role_id: int,
    menu_ids: list[int],
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(require_platform_admin()),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    if role.is_system:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="System roles cannot be modified"
        )
    crud_role.update_menus(db, role_id=role_id, menu_ids=menu_ids)
    return {"message": "Role menus updated successfully"}


@router.post("/custom", response_model=RoleResponse, status_code=status.HTTP_201_CREATED)
def create_custom_role(
    role_in: RoleCreate,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role = RoleService.create_custom_role(
        db=db,
        name=role_in.name,
        description=role_in.description,
        tenant_id=current_user.tenant_id,
        created_by=current_user.id,
    )
    return role


@router.get("/tenant/list", response_model=list[RoleResponse])
def list_tenant_roles(
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role_service = RoleService(db)
    roles = role_service.get_tenant_roles(current_user.tenant_id)
    return roles


@router.put("/{role_id}/custom", response_model=RoleResponse)
def update_custom_role(
    role_id: int,
    role_in: RoleUpdate,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role_service = RoleService(db)
    role = role_service.update_custom_role(
        role_id=role_id,
        tenant_id=current_user.tenant_id,
        name=role_in.name,
        description=role_in.description,
    )
    return role


@router.delete("/{role_id}/custom", status_code=status.HTTP_204_NO_CONTENT)
def delete_custom_role(
    role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role_service = RoleService(db)
    role_service.delete_custom_role(role_id, current_user.tenant_id)
    return None


@router.post("/{role_id}/permissions", response_model=dict)
def assign_role_permissions(
    role_id: int,
    permissions_in: RolePermissionsUpdate,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    if role.tenant_id != current_user.tenant_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="No permission to modify this role"
        )
    if role.is_system:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="System roles cannot be modified"
        )
    permission_service = PermissionService(db)
    permission_service.assign_role_permissions(
        role_id=role_id,
        tenant_id=current_user.tenant_id,
        permissions=[(p.resource, p.action) for p in permissions_in.permissions],
    )
    logger.bind(user_id=current_user.id, role_id=role_id).info("Role permissions assigned")
    return {"message": "Permissions assigned successfully"}


@router.get("/{role_id}/permissions", response_model=list[RolePermissionResponse])
def get_role_permissions(
    role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    role = crud_role.get(db, id=role_id)
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
    if role.tenant_id != current_user.tenant_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="No permission to view this role"
        )
    permission_service = PermissionService(db)
    permissions = permission_service.get_role_permissions(role_id, current_user.tenant_id)
    return [{"resource": p[0], "action": p[1]} for p in permissions]


@router.get("/permissions/resources", response_model=list[ResourceInfo])
def get_available_resources(current_user: User = Depends(deps.get_current_user)):
    permission_service = PermissionService(None)
    resources = permission_service.get_available_resources()
    return [{"name": name, "actions": actions} for name, actions in resources.items()]


@router.get("/{role_id}/permissions/export")
def export_permissions(
    role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    template_service = PermissionTemplateService()
    config = template_service.export_role_permissions(db, role_id, current_user.tenant_id)
    return config


@router.post("/{role_id}/permissions/import")
def import_permissions(
    role_id: int,
    config: dict,
    replace: bool = False,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    template_service = PermissionTemplateService()
    template_service.import_role_permissions(db, role_id, current_user.tenant_id, config, replace)
    return {"message": "Permissions imported successfully"}


@router.post("/{role_id}/permissions/copy")
def copy_permissions(
    role_id: int,
    from_role_id: int,
    db: Session = Depends(deps.get_db_without_tenant),
    current_user: User = Depends(deps.get_current_user),
):
    template_service = PermissionTemplateService()
    template_service.copy_permissions(db, from_role_id, role_id, current_user.tenant_id)
    return {"message": "Permissions copied successfully"}
