from typing import Dict, List, Set

from sqlalchemy import or_
from sqlalchemy.orm import Session

from src.apps.role_permission.data.permissions_data import (
    DEFAULT_PERMISSIONS,
    DEFAULT_ROLES,
)
from src.apps.role_permission.models.permission_model import Permission
from src.apps.role_permission.models.role_model import Role, roles_permissions_map
from src.apps.role_permission.models.user_permission_model import UserPermissions
from src.apps.role_permission.schemas.requests import RoleCreateRequestSchema
from src.apps.role_permission.services.role_service import create_role
from src.apps.user.models.user_model import Users


async def get_user_permissions(db: Session, user_id: str) -> Set[str]:
    """
    Get all permission operations for a user
    Returns a set of permission operation strings
    """
    user = db.query(Users).filter(Users.user_id == user_id).first()
    if not user:
        return set()

    user_permissions = db.query(UserPermissions).filter(UserPermissions.user_id == user.id).all()

    role_ids = [up.role_id for up in user_permissions if up.role_id is not None]
    direct_permission_ids = [up.permission_id for up in user_permissions if up.permission_id is not None]

    role_permission_entries = (
        db.query(roles_permissions_map).filter(roles_permissions_map.c.role_id.in_(role_ids)).all() if role_ids else []
    )

    role_permission_ids = [rp.permission_id for rp in role_permission_entries]

    all_permission_ids = set(direct_permission_ids + role_permission_ids)

    permissions = db.query(Permission).filter(Permission.id.in_(all_permission_ids)).all() if all_permission_ids else []

    permission_operations = {p.operation for p in permissions if p.is_active}

    return permission_operations


async def seed_default_permissions(db: Session) -> None:
    """
    Create default permissions in the database using bulk operations
    This function should be called during application startup
    """

    existing_permissions = {(p.module, p.operation): p for p in db.query(Permission).all()}

    permissions_to_add = []

    permissions_to_add = list(
        map(
            lambda perm: Permission(
                module=perm["module"],
                submodule=perm["submodule"],
                operation=perm["operation"],
                operation_label=perm["operation_label"],
                display_order=perm["display_order"],
                is_active=True,
            ),
            filter(lambda perm: (perm["module"], perm["operation"]) not in existing_permissions, DEFAULT_PERMISSIONS),
        )
    )

    if permissions_to_add:
        db.add_all(permissions_to_add)
        db.commit()


async def create_admin_role_with_all_permissions(db: Session) -> None:
    """
    Create default roles with their permissions from the data file
    This function will create roles regardless of whether users exist
    """

    seeding_user_dict = {"id": None, "user_id": "system"}

    all_permissions = db.query(Permission).filter(Permission.is_active == True).all()
    all_permission_ids = {p.operation: p.id for p in all_permissions}

    for role_def in DEFAULT_ROLES:
        existing_role = db.query(Role).filter(Role.slug == role_def["slug"]).first()
        if existing_role:
            continue

        if role_def["permissions"] == ["*"]:
            permission_ids = [p.id for p in all_permissions]
        else:
            permission_ids = [all_permission_ids[op] for op in role_def["permissions"] if op in all_permission_ids]

        role_payload = RoleCreateRequestSchema(
            label=role_def["label"],
            slug=role_def["slug"],
            is_default=role_def.get("is_default", True),
            permissions=permission_ids,
        )

        try:
            await create_role(
                db=db,
                payload=role_payload,
                current_user=seeding_user_dict,
                return_raw=True,
                is_seeding=True,
                use_provided_slug=True,
            )
        except Exception as e:
            print(f"Failed to create role {role_def['label']}: {str(e)}")
            pass
