"""
Admin Configuration Routes
"""
from datetime import datetime
import time
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from sqlalchemy.exc import OperationalError
from pydantic import BaseModel
from typing import Optional, List, Dict, Any

from app.core.database import get_db, SessionLocal
from app.models.admin import AdminConfiguration
from app.services.agent_config import get_all_admin_configs
from app.core.dependencies import get_current_user
from app.models.user import User
from fastapi.concurrency import run_in_threadpool

# Schemas
class AdminConfigCreate(BaseModel):
    configuration_name: str
    type: str
    value: str


class AdminConfigResponse(BaseModel):
    id: int
    configuration_name: Optional[str]
    type: Optional[str]
    value: Optional[str]
    created_at: Optional[str]
    updated_at: Optional[str]
    class Config:
        from_attributes = True


class OrganisationDetailsRequest(BaseModel):
    agent_id: str
    user_id: str


class OrganisationDetailsResponse(BaseModel):
    success: bool
    message: str
    details: Dict[str, Any]


class UpdateOrganisationDetailsRequest(BaseModel):
    agent_id: str
    user_id: str
    id: int  # Configuration ID
    value: str  # New configuration value


class UpdateOrganisationDetailsResponse(BaseModel):
    success: bool
    message: str
    details: Dict[str, Any]


class CreateAdminConfigRequest(BaseModel):
    agent_id: str
    user_id: str
    type: str  # Configuration name/type (e.g., "ORGANIZATIONAL DOMAIN", "ORGANIZATIONAL NAME", "ORGANIZATIONAL TYPE")
    value: str  # Configuration value


class CreateAdminConfigResponse(BaseModel):
    success: bool
    message: str
    details: Dict[str, Any]


class UpdateAdminConfigRequest(BaseModel):
    agent_id: str
    user_id: str
    id: int  # Configuration ID
    type: str  # Configuration name/type (e.g., "ORGANIZATIONAL DOMAIN", "ORGANIZATIONAL NAME", "ORGANIZATIONAL TYPE")
    value: str  # Configuration value


class UpdateAdminConfigResponse(BaseModel):
    success: bool
    message: str
    details: Dict[str, Any]


class DeleteAdminConfigRequest(BaseModel):
    agent_id: str
    user_id: str
    id: int  # Configuration ID


class DeleteAdminConfigResponse(BaseModel):
    success: bool
    message: str
    details: Dict[str, Any]


class GetAllAdminConfigsRequest(BaseModel):
    agent_id: str
    user_id: str


class GetAllAdminConfigsResponse(BaseModel):
    success: bool
    message: str
    details: Dict[str, Any]


router = APIRouter(prefix="/admin", tags=["Admin"])





@router.post("/organisation-details", response_model=OrganisationDetailsResponse)
async def get_organisation_details(
    request: OrganisationDetailsRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    Get all organisation details (configuration type "C").
    
    Mirrors the functionality from updated/admin_configuration/organisation_details.py
    Returns all configuration entries with type="C" (core/organisation configs).
    
    Args:
        request: OrganisationDetailsRequest containing agent_id and user_id
        db: Database session
    
    Returns:
        OrganisationDetailsResponse with success, message, and details containing organisation_details list
    """
    try:
        # Get all organisation configurations (type="C")
        org_configs = await run_in_threadpool(get_all_admin_configs, config_type="C")
        
        # Transform to match the structure from organisation_details.py
        # Format: {id, name, value} where name is configuration_name
        # Mask value field based on length, except for ORG_ID, ORG_NAME, SENDER_EMAIL, BASE_URL
        def mask_value(name, value):
            """
            Mask value based on character length:
            - If char > 3: Show first 2 chars, mask rest
            - If char <= 3: Show first char, mask rest
            - Exception: ORG_ID, ORG_NAME, SENDER_EMAIL, BASE_URL are unmasked
            """
            # Unmasked fields
            if name in ["ORG_ID", "ORG_NAME", "SENDER_EMAIL", "BASE_URL"]:
                return value
            
            if value:
                value_str = str(value)
                value_len = len(value_str)
                
                if value_len > 3:
                    # Show first 2 characters, mask rest
                    first_two = value_str[:2]
                    masked_rest = '*' * (value_len - 2)
                    return first_two + masked_rest
                else:
                    # Show only first character, mask rest
                    return value_str[0] + '*' * (value_len - 1)
            
            return value
        
        organisation_details = [
            {
                "id": config.get("id"),
                "name": config.get("configuration_name"),
                "value": mask_value(config.get("configuration_name"), config.get("value")),
                "type": config.get("type"),
                "created_at": config.get("created_at"),
                "updated_at": config.get("updated_at")
            }
            for config in org_configs
        ]
        
        return {
            "success": True,
            "message": "Organisation details retrieved successfully",
            "details": {
                "organisation_details": organisation_details
            }
        }
    except Exception as e:
        return {
            "success": False,
            "message": f"Error retrieving organisation details: {str(e)}",
            "details": {}
        }




@router.post("/update-organisation-details", response_model=UpdateOrganisationDetailsResponse)
async def update_organisation_details(
    request: UpdateOrganisationDetailsRequest,
    db: Session = Depends(get_db)
):
    """
    Update an existing organisation detail (configuration type "C").
    
    Mirrors the functionality from updated/admin_configuration/organisation_details.py
    Updates a configuration entry with type="C" (core/organisation configs).
    Uses retry logic for database locks to prevent write conflicts.
    
    Args:
        request: UpdateOrganisationDetailsRequest containing agent_id, user_id, id, and value
        db: Database session
    
    Returns:
        UpdateOrganisationDetailsResponse with success, message, and details containing the updated configuration
    """
    # Validate that value is not empty (before retry loop)
    if not request.value or not request.value.strip():
        return {
            "success": False,
            "message": "Value cannot be empty",
            "details": {}
        }
    
    max_retries = 5
    retry_delay = 0.1  # Start with 100ms delay
    
    # Use the provided session for the first attempt
    current_db = db
    
    for attempt in range(max_retries):
        try:
            # Create a new session for retries (after first attempt)
            if attempt > 0:
                current_db = await run_in_threadpool(SessionLocal)
            
            try:
                # Check if configuration exists and has type="C" (organisation config)
                def get_config_from_db():
                    return current_db.query(AdminConfiguration).filter(
                        AdminConfiguration.id == request.id,
                        AdminConfiguration.type == "C"
                    ).first()
                
                config = await run_in_threadpool(get_config_from_db)
                
                if not config:
                    return {
                        "success": False,
                        "message": f"Organisation configuration with id {request.id} not found or is not an organisation configuration",
                        "details": {}
                    }
                
                # Create timestamp in the same format as save_local_db.py
                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                
                # Update the configuration entry
                # Keep configuration_name and type="C" as they are, only update value
                def update_config_in_db():
                    config.value = request.value.strip()
                    config.updated_at = current_time
                    current_db.commit()
                    current_db.refresh(config)
                    return config
                
                config = await run_in_threadpool(update_config_in_db)
                
                # Return in the same format as get_organisation_details
                result = {
                    "success": True,
                    "message": "Organisation detail updated successfully",
                    "details": {
                        "organisation_detail": {
                            "id": config.id,
                            "name": config.configuration_name,
                            "value": config.value,
                            "type": config.type,
                            "created_at": config.created_at,
                            "updated_at": config.updated_at
                        }
                    }
                }
                
                return result
                
            except HTTPException:
                raise
            except OperationalError as e:
                await run_in_threadpool(current_db.rollback)
                # Check if it's a database locked error
                if "database is locked" in str(e).lower() and attempt < max_retries - 1:
                    # Exponential backoff: wait longer for each retry
                    time.sleep(retry_delay * (2 ** attempt))
                    continue
                else:
                    return {
                        "success": False,
                        "message": f"Error updating organisation detail: Database is locked. Please try again later. {str(e)}",
                        "details": {}
                    }
            except Exception as e:
                await run_in_threadpool(current_db.rollback)
                return {
                    "success": False,
                    "message": f"Error updating organisation detail: {str(e)}",
                    "details": {}
                }
        except HTTPException:
            raise
        except Exception as e:
            return {
                "success": False,
                "message": f"Error updating organisation detail: {str(e)}",
                "details": {}
            }
    
    # If we've exhausted all retries
    return {
        "success": False,
        "message": "Error updating organisation detail: Database is locked after multiple retries. Please try again later.",
        "details": {}
    }


@router.post("/get-all-admin-configs", response_model=GetAllAdminConfigsResponse)
async def get_all_admin_configs_api(
    request: GetAllAdminConfigsRequest,
    db: Session = Depends(get_db)
):
    """
    Get all admin configurations (configuration type "O").
    
    Mirrors the functionality from updated/admin_configuration/admin_configuration_tab.py
    Returns all configuration entries with type="O" (admin options).
    
    Args:
        request: GetAllAdminConfigsRequest containing agent_id and user_id
        db: Database session
    
    Returns:
        GetAllAdminConfigsResponse with success, message, and details containing admin_configs list
    """
    try:
        # Get all admin configurations (type="O")
        admin_configs = await run_in_threadpool(get_all_admin_configs, config_type="O")
        
        # Transform to match the structure from admin_configuration_tab.py
        # Format: {id, name, value} where name is configuration_name
        admin_configs_list = [
            {
                "id": config.get("id"),
                "name": config.get("configuration_name"),
                "value": config.get("value"),
                "type": config.get("type"),
                "created_at": config.get("created_at"),
                "updated_at": config.get("updated_at")
            }
            for config in admin_configs
        ]
        
        return {
            "success": True,
            "message": "Admin configurations retrieved successfully",
            "details": {
                "admin_configs": admin_configs_list
            }
        }
    except Exception as e:
        return {
            "success": False,
            "message": f"Error retrieving admin configurations: {str(e)}",
            "details": {}
        }

@router.post("/create-config", response_model=CreateAdminConfigResponse, status_code=201)
async def create_admin_config(
    request: CreateAdminConfigRequest,
    db: Session = Depends(get_db)
):
    """
    Create a new admin configuration.
    
    Mirrors the functionality from updated/admin_configuration/admin_configuration_tab.py
    Creates a configuration entry with type="O" (admin options).
    Uses retry logic for database locks to prevent write conflicts.
    
    Valid type values (from NAME_OPTIONS):
    - "ORGANIZATIONAL DOMAIN"
    - "ORGANIZATIONAL NAME"
    - "ORGANIZATIONAL TYPE"
    
    Args:
        request: CreateAdminConfigRequest containing agent_id, user_id, type, and value
        db: Database session
    
    Returns:
        CreateAdminConfigResponse with success, message, and details containing the created configuration
    """
    # Validate that value is not empty (before retry loop)
    if not request.value or not request.value.strip():
        return {
            "success": False,
            "message": "Value cannot be empty",
            "details": {}
        }

    max_retries = 5
    retry_delay = 0.1  # Start with 100ms delay
    
    # Use the provided session for the first attempt
    current_db = db
    
    for attempt in range(max_retries):
        try:
            # Create a new session for retries (after first attempt)
            if attempt > 0:
                current_db = await run_in_threadpool(SessionLocal)
            
            try:
                # Create timestamp in the same format as save_local_db.py
                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                
                # Create the configuration entry
                # config_type is "O" for admin options (as per admin_configuration_tab.py)
                config = AdminConfiguration(
                    configuration_name=request.type,
                    type="O",  # Hardcoded as "O" for admin options
                    value=request.value.strip(),
                    created_at=current_time,
                    updated_at=current_time
                )
                
                def add_confg_to_db():
                    current_db.add(config)
                    current_db.commit()
                    current_db.refresh(config)
                    return config
                
                config = await run_in_threadpool(add_confg_to_db)
                
                result = {
                    "success": True,
                    "message": "Admin configuration created successfully",
                    "details": {
                        "configuration": {
                            "id": config.id,
                            "name": config.configuration_name,
                            "type": config.type,
                            "value": config.value,
                            "created_at": config.created_at,
                            "updated_at": config.updated_at
                        }
                    }
                }
                
                return result
                
            except OperationalError as e:
                await run_in_threadpool(current_db.rollback)
                # Check if it's a database locked error
                if "database is locked" in str(e).lower() and attempt < max_retries - 1:
                    # Exponential backoff: wait longer for each retry
                    time.sleep(retry_delay * (2 ** attempt))
                    continue
                else:
                    return {
                        "success": False,
                        "message": f"Error creating admin configuration: Database is locked. Please try again later. {str(e)}",
                        "details": {}
                    }
            except Exception as e:
                await run_in_threadpool(current_db.rollback)
                return {
                    "success": False,
                    "message": f"Error creating admin configuration: {str(e)}",
                    "details": {}
                }
        except HTTPException:
            raise
        except Exception as e:
            return {
                "success": False,
                "message": f"Error creating admin configuration: {str(e)}",
                "details": {}
            }
    
    # If we've exhausted all retries
    return {
        "success": False,
        "message": "Error creating admin configuration: Database is locked after multiple retries. Please try again later.",
        "details": {}
    }


@router.post("/update-config", response_model=UpdateAdminConfigResponse)
async def update_admin_config(
    request: UpdateAdminConfigRequest,
    db: Session = Depends(get_db)
):
    """
    Update an existing admin configuration.
    
    Mirrors the functionality from updated/admin_configuration/admin_configuration_tab.py
    Updates a configuration entry with type="O" (admin options).
    Uses retry logic for database locks to prevent write conflicts.
    
    Valid type values (from NAME_OPTIONS):
    - "ORGANIZATIONAL DOMAIN"
    - "ORGANIZATIONAL NAME"
    - "ORGANIZATIONAL TYPE"
    
    Args:
        request: UpdateAdminConfigRequest containing agent_id, user_id, id, type, and value
        db: Database session
    
    Returns:
        UpdateAdminConfigResponse with success, message, and details containing the updated configuration
    """
    # Validate that value is not empty (before retry loop)
    if not request.value or not request.value.strip():
        return {
            "success": False,
            "message": "Value cannot be empty",
            "details": {}
        }
    
    max_retries = 5
    retry_delay = 0.1  # Start with 100ms delay
    
    # Use the provided session for the first attempt
    current_db = db
    
    for attempt in range(max_retries):
        try:
            # Create a new session for retries (after first attempt)
            if attempt > 0:
                current_db = await run_in_threadpool(SessionLocal)
            
            try:
                # Check if configuration exists
                def get_config_from_db():
                    return current_db.query(AdminConfiguration).filter(
                        AdminConfiguration.id == request.id
                    ).first()
                
                config = await run_in_threadpool(get_config_from_db)
                
                if not config:
                    return {
                        "success": False,
                        "message": f"Configuration with id {request.id} not found",
                        "details": {}
                    }
                
                # Create timestamp in the same format as save_local_db.py
                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                
                # Update the configuration entry
                # config_type is "O" for admin options (as per admin_configuration_tab.py)
                def update_config_in_db():
                    config.configuration_name = request.type
                    config.type = "O"  # Hardcoded as "O" for admin options
                    config.value = request.value.strip()
                    config.updated_at = current_time
                    
                    current_db.commit()
                    current_db.refresh(config)
                    return config
                
                config = await run_in_threadpool(update_config_in_db)
                
                result = {
                    "success": True,
                    "message": "Admin configuration updated successfully",
                    "details": {
                        "configuration": {
                            "id": config.id,
                            "name": config.configuration_name,
                            "type": config.type,
                            "value": config.value,
                            "created_at": config.created_at,
                            "updated_at": config.updated_at
                        }
                    }
                }
                
                return result
                
            except HTTPException:
                raise
            except OperationalError as e:
                await run_in_threadpool(current_db.rollback)
                # Check if it's a database locked error
                if "database is locked" in str(e).lower() and attempt < max_retries - 1:
                    # Exponential backoff: wait longer for each retry
                    time.sleep(retry_delay * (2 ** attempt))
                    continue
                else:
                    return {
                        "success": False,
                        "message": f"Error updating admin configuration: Database is locked. Please try again later. {str(e)}",
                        "details": {}
                    }
            except Exception as e:
                await run_in_threadpool(current_db.rollback)
                return {
                    "success": False,
                    "message": f"Error updating admin configuration: {str(e)}",
                    "details": {}
                }
        except HTTPException:
            raise
        except Exception as e:
            return {
                "success": False,
                "message": f"Error updating admin configuration: {str(e)}",
                "details": {}
            }
    
    # If we've exhausted all retries
    return {
        "success": False,
        "message": "Error updating admin configuration: Database is locked after multiple retries. Please try again later.",
        "details": {}
    }


@router.post("/delete-config", response_model=DeleteAdminConfigResponse)
async def delete_admin_config(
    request: DeleteAdminConfigRequest,
    db: Session = Depends(get_db)
):
    """
    Delete an existing admin configuration.
    
    Mirrors the functionality from updated/admin_configuration/admin_configuration_tab.py
    Deletes a configuration entry by ID.
    Uses retry logic for database locks to prevent write conflicts.
    
    Args:
        request: DeleteAdminConfigRequest containing agent_id, user_id, and id
        db: Database session
    
    Returns:
        DeleteAdminConfigResponse with success, message, and details containing the deleted configuration id
    """
    max_retries = 5
    retry_delay = 0.1  # Start with 100ms delay
    
    # Use the provided session for the first attempt
    current_db = db
    
    for attempt in range(max_retries):
        try:
            # Create a new session for retries (after first attempt)
            if attempt > 0:
                current_db = await run_in_threadpool(SessionLocal)
            
            try:
                # Check if configuration exists
                def get_config_from_db():
                    return current_db.query(AdminConfiguration).filter(
                        AdminConfiguration.id == request.id
                    ).first()
                
                config = await run_in_threadpool(get_config_from_db)
                
                if not config:
                    return {
                        "success": False,
                        "message": f"Configuration with id {request.id} not found",
                        "details": {}
                    }
                
                # Store config details before deletion for response
                config_id = config.id
                config_name = config.configuration_name
                config_value = config.value
                
                # Delete the configuration entry
                def delete_config_from_db():
                    current_db.delete(config)
                    current_db.commit()
                
                await run_in_threadpool(delete_config_from_db)
                
                result = {
                    "success": True,
                    "message": "Admin configuration deleted successfully",
                    "details": {
                        "id": config_id,
                        "name": config_name,
                        "value": config_value
                    }
                }
                
                return result
                
            except HTTPException:
                raise
            except OperationalError as e:
                await run_in_threadpool(current_db.rollback)
                # Check if it's a database locked error
                if "database is locked" in str(e).lower() and attempt < max_retries - 1:
                    # Exponential backoff: wait longer for each retry
                    time.sleep(retry_delay * (2 ** attempt))
                    continue
                else:
                    return {
                        "success": False,
                        "message": f"Error deleting admin configuration: Database is locked. Please try again later. {str(e)}",
                        "details": {}
                    }
            except Exception as e:
                await run_in_threadpool(current_db.rollback)
                return {
                    "success": False,
                    "message": f"Error deleting admin configuration: {str(e)}",
                    "details": {}
                }
        except HTTPException:
            raise
        except Exception as e:
            return {
                "success": False,
                "message": f"Error deleting admin configuration: {str(e)}",
                "details": {}
            }
    
    # If we've exhausted all retries
    return {
        "success": False,
        "message": "Error deleting admin configuration: Database is locked after multiple retries. Please try again later.",
        "details": {}
    }
