from datetime import datetime
from pymongo.errors import DuplicateKeyError
from app.db import database
from app.v1.models.saas.rolemodel import RoleCreate, RoleUpdate
from typing import Optional

COLLECTION_NAME = "roles"

def get_next_role_id(db, role_type: str) -> int:
    """
    Generate the next role_id based on the role type.
    
    For SaaS roles, the counter (_id = "role_id_saas") must not exceed 99.
    For Platform roles, the counter (_id = "role_id_platform") must not exceed 999999.
    If the counter document is missing, it will be initialized based on the
    highest existing role_id in the roles collection.
    """
    counter_id = "role_id_saas" if role_type == "saas" else "role_id_platform"
    max_limit = 99 if role_type == "saas" else 999999
    counters_collection = db["counters"]

    # If the counter does not exist, initialize it from existing roles.
    counter = counters_collection.find_one({"_id": counter_id})
    if counter is None:
        roles_collection = db[COLLECTION_NAME]
        query = {"is_saas_only": True} if role_type == "saas" else {"is_saas_only": {"$ne": True}}
        max_role_cursor = roles_collection.find(query).sort("role_id", -1).limit(1)
        starting_value = 0
        for role in max_role_cursor:
            starting_value = role.get("role_id", 0)
        # Insert a new counter document with the starting value.
        counters_collection.insert_one({"_id": counter_id, "sequence_value": starting_value})
    
    # Update the counter document and increment sequence_value by 1.
    updated = counters_collection.find_one_and_update(
        {"_id": counter_id},
        {"$inc": {"sequence_value": 1}},
        return_document=True
    )
    new_role_id = updated["sequence_value"]
    if new_role_id > max_limit:
        raise ValueError(f"Role ID for {role_type} roles exceeded the maximum limit of {max_limit}.")
    return new_role_id

def _convert_permissions_keys(permissions):
    # Convert all keys in the permissions dict to strings
    return {str(k): v for k, v in permissions.items()} if permissions else permissions

def create_role_service(role: RoleCreate, db: database.MongoDB) -> dict:
    roles_collection = db[COLLECTION_NAME]
    role_data = role.dict()
    
    # Convert permission keys to strings (if permissions exist)
    if "permissions" in role_data and role_data["permissions"]:
        role_data["permissions"] = _convert_permissions_keys(role_data["permissions"])
    
    role_data["created_date"] = datetime.utcnow()
    role_data["updated_date"] = datetime.utcnow()

    # Determine role type: SaaS roles have is_saas_only=True; otherwise assume platform role.
    role_type = "saas" if role_data.get("is_saas_only") else "platform"
    if not role_data.get("role_id"):
        role_data["role_id"] = get_next_role_id(db, role_type)

    try:
        roles_collection.insert_one(role_data)
    except DuplicateKeyError:
        raise ValueError("A role with this name already exists.")

    return role_data

def list_roles_service(
    db: database.MongoDB, 
    skip: int, 
    limit: int, 
    account_id: Optional[str] = None, 
    search: Optional[str] = None,
    is_system_default: bool = False,
    is_saas_only: bool = False
) -> dict:
    roles_collection = db[COLLECTION_NAME]
    query = {"is_system_default": is_system_default}
    
    if account_id:
        query["account_id"] = account_id
    else:
        query["$or"] = [{"account_id": {"$exists": False}}, {"account_id": ""}, {"account_id": None}]

    if search:
        query["name"] = {"$regex": search, "$options": "i"}

    # Exclude SaaS-only roles for platform admins
    if is_system_default and not is_saas_only:
        query["is_saas_only"] = {"$ne": True}

    roles_cursor = roles_collection.find(query).skip(skip).limit(limit)

    roles = []
    for role in roles_cursor:
        role["id"] = role.get("role_id")
        role["created_date"] = role["created_date"].isoformat()
        role["updated_date"] = role["updated_date"].isoformat()
        roles.append(role)

    total_count = roles_collection.count_documents(query)

    return {"total_count": total_count, "roles": roles}

def get_role_service(role_id: int, db: database.MongoDB) -> dict:
    roles_collection = db[COLLECTION_NAME]
    role = roles_collection.find_one({"role_id": role_id})
    if role:
        role["id"] = role.get("role_id")
        role["created_date"] = role["created_date"].isoformat()
        role["updated_date"] = role["updated_date"].isoformat()

    return role

def update_role_service(role_id: int, role_update: RoleUpdate, db: database.MongoDB) -> dict:
    roles_collection = db[COLLECTION_NAME]
    update_data = role_update.dict(exclude_unset=True)
    
    # Convert permission keys to strings if present in update_data
    if "permissions" in update_data and update_data["permissions"]:
        update_data["permissions"] = _convert_permissions_keys(update_data["permissions"])
    
    update_data["updated_date"] = datetime.utcnow()

    result = roles_collection.update_one({"role_id": role_id}, {"$set": update_data})
    if result.matched_count == 0:
        return None

    role = roles_collection.find_one({"role_id": role_id})
    if role:
        role["id"] = role.get("role_id")
        role["created_date"] = role["created_date"].isoformat()
        role["updated_date"] = role["updated_date"].isoformat()
    return role

def delete_role_service(role_id: int, db: database.MongoDB) -> dict:
    roles_collection = db[COLLECTION_NAME]
    role = roles_collection.find_one({"role_id": role_id})
    if not role:
        return None
    roles_collection.delete_one({"role_id": role_id})
    role["id"] = role.get("role_id")
    role["created_date"] = role["created_date"].isoformat()
    role["updated_date"] = role["updated_date"].isoformat()
    return role
