from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db import transaction, connection
from .models import Configuration, ConfigurationTranslation, AppSettings
from .serializers import MenuSerializer, AppSettingsSerializer
from questionconfigurations.models import QuestionConfiguration
from ErrorLogs.utils import log_error
import json


def get_user_language(request):
    """
    Get user's language preference from request.
    Returns 'en' as default if user is not authenticated or language is not set.
    Fetches fresh from database to ensure we get the latest language setting.
    """
    try:
        # First try to get from request.user (cached, fast) - this is the most reliable
        if request.user and hasattr(request.user, 'lang'):
            cached_lang = request.user.lang
            if cached_lang:
                # Try to fetch fresh from database, but don't fail if it doesn't work
                try:
                    if hasattr(request.user, 'id') and request.user.id:
                        from user.models import User
                        user = User.objects.filter(id=request.user.id).only('lang').first()
                        if user and user.lang:
                            return user.lang
                except Exception:
                    # If database fetch fails, use cached value - this ensures API still works
                    pass
                return cached_lang or 'en'
        
        # If no cached value, try database fetch
        if request.user and hasattr(request.user, 'id') and request.user.id:
            try:
                from user.models import User
                user = User.objects.filter(id=request.user.id).only('lang').first()
                if user and user.lang:
                    return user.lang
            except Exception:
                pass
    except Exception:
        # If anything goes wrong, just return default
        pass
    
    return 'en'


def apply_translations(config_data, questions_data, user_language):
    """
    Apply translations to configuration header and question labels.
    
    Args:
        config_data: Dictionary with configuration data (header, icon, color, etc.)
        questions_data: List of question dictionaries
        user_language: User's language code ('en', 'fi', 'sv')
    
    Returns:
        Tuple of (translated_config_data, translated_questions_data)
    """
    # If language is 'en', no translation needed (default language)
    if user_language == 'en':
        return config_data, questions_data
    
    function_ref_id = config_data.get('function_ref_id') if isinstance(config_data, dict) else None
    if not function_ref_id:
        # Try to get from the config object if it's a Configuration instance
        if hasattr(config_data, 'function_ref_id'):
            function_ref_id = config_data.function_ref_id
        else:
            return config_data, questions_data
    
    # Get translations for function header
    try:
        header_translation = ConfigurationTranslation.objects.filter(
            translation_type='function_header',
            language=user_language,
            reference_id=function_ref_id
        ).first()
        
        if header_translation and isinstance(config_data, dict):
            config_data = config_data.copy()
            config_data['header'] = header_translation.translated_text
    except Exception:
        pass  # If translation lookup fails, use original
    
    # Get translations for question labels
    translated_questions = []
    for question in questions_data:
        question_id = question.get('question_id') or ''
        translated_question = question.copy()
        
        if question_id:
            try:
                label_translation = ConfigurationTranslation.objects.filter(
                    translation_type='question_label',
                    language=user_language,
                    reference_id=question_id
                ).first()
                
                if label_translation:
                    translated_question['label'] = label_translation.translated_text
            except Exception:
                pass  # If translation lookup fails, use original
        
        translated_questions.append(translated_question)
    
    return config_data, translated_questions


def resolve_dependency_in_sql(custom_sql, dependency_id, question_configs, answers):
    """
    Replace :dependencyId in custom_sql with the actual value from answers.
    
    Args:
        custom_sql: The SQL query string that may contain :dependencyId
        dependency_id: The question_id of the question this depends on
        question_configs: All question configurations for this function_ref_id
        answers: Dictionary of answers where keys are question labels or question_ids
    
    Returns:
        The SQL query with :dependencyId replaced, or original SQL if dependency not found
    """
    # Check if SQL has any dependency placeholder (could be :dependencyId, :project_id, :task_id, etc.)
    # The placeholder format is :<dependency_id> where dependency_id is the question_id
    has_dependency_placeholder = False
    placeholder_to_replace = None
    
    if custom_sql and dependency_id:
        # Check for :dependencyId (generic placeholder)
        if ':dependencyId' in custom_sql:
            has_dependency_placeholder = True
            placeholder_to_replace = ':dependencyId'
        # Check for :<dependency_id> (specific placeholder like :project_id, :task_id, etc.)
        elif f':{dependency_id}' in custom_sql:
            has_dependency_placeholder = True
            placeholder_to_replace = f':{dependency_id}'
    
    if not custom_sql or not has_dependency_placeholder:
        return custom_sql
    
    if not dependency_id:
        return custom_sql
    
    # Find the dependent question by question_id
    dependent_question = None
    for qc in question_configs:
        if qc.question_id == dependency_id:
            dependent_question = qc
            break
    
    if not dependent_question:
        # Dependency question not found, return original SQL
        return custom_sql
    
    # Try to get the value from answers using label first, then question_id
    dependency_value = None
    if dependent_question.label in answers:
        dependency_value = answers[dependent_question.label]
    elif dependency_id in answers:
        dependency_value = answers[dependency_id]
    
    if dependency_value is None:
        # No value found for dependency, return original SQL (or empty result)
        return custom_sql
    
    # Replace the placeholder with the actual value
    # placeholder_to_replace is already determined above (either :dependencyId or :<dependency_id>)
    
    # Check if the placeholder is already quoted in the SQL (e.g., ':project_id' or ":project_id")
    # If so, replace the quoted version; otherwise, add quotes
    quoted_placeholder_single = f"'{placeholder_to_replace}'"
    quoted_placeholder_double = f'"{placeholder_to_replace}"'
    
    # Escape single quotes to prevent SQL injection
    if isinstance(dependency_value, str):
        # Escape single quotes (for SQL)
        escaped_value = dependency_value.replace("'", "''")
        value_to_insert = f"'{escaped_value}'"
    else:
        # For non-string values, use directly
        value_to_insert = str(dependency_value)
    
    # Try to replace quoted versions first, then unquoted
    if quoted_placeholder_single in custom_sql:
        resolved_sql = custom_sql.replace(quoted_placeholder_single, value_to_insert)
    elif quoted_placeholder_double in custom_sql:
        # If double quotes, use double quotes for the value too
        if isinstance(dependency_value, str):
            escaped_value = dependency_value.replace('"', '""')
            value_to_insert = f'"{escaped_value}"'
        resolved_sql = custom_sql.replace(quoted_placeholder_double, value_to_insert)
    else:
        # No quotes around placeholder, replace directly (SQL should handle quoting)
        resolved_sql = custom_sql.replace(placeholder_to_replace, value_to_insert)
    
    return resolved_sql

@api_view(['GET', 'POST'])
def configuration_list(request):
    if request.method == 'GET':
        try:
            configurations = Configuration.objects.all()
            
            # Get user's language preference
            user_language = get_user_language(request)
            
            # Prepare response data in the requested format
            response_data = {
                'InMenu': {},
                'OutMenu': {}
            }
            
            for config in configurations:
                # Get all question configurations for this function_ref_id, ordered by order field
                question_configs = QuestionConfiguration.objects.filter(
                    function_ref_id=config.function_ref_id
                ).order_by('order', 'created_at')
                
                # Get answers from query parameters (for dependency resolution)
                answers = {}
                if 'answers' in request.query_params:
                    try:
                        answers = json.loads(request.query_params['answers'])
                    except (json.JSONDecodeError, TypeError):
                        pass
                else:
                    # Also check for individual answer parameters
                    for key, value in request.query_params.items():
                        if key.startswith('answer_'):
                            answer_key = key.replace('answer_', '')
                            answers[answer_key] = value
                
                questions = []
                for qc in question_configs:
                    question_data = {
                        "label": qc.label,
                        "read_only": qc.read_only,
                        "required": qc.required,
                        "question_type": qc.question_type,
                        "question_options": qc.question_options,
                        "border_color": qc.border_color,
                        "custom_sql": qc.custom_sql,
                        "order": qc.order,
                        "question_category": qc.question_category or 'None',
                        "question_id": qc.question_id or '',
                        "dependency_id": qc.dependency_id or ''
                    }
                    
                    # If custom_sql is present, execute it and update question_options
                    if qc.custom_sql:
                        try:
                            # Resolve dependency if present
                            resolved_sql = resolve_dependency_in_sql(
                                qc.custom_sql,
                                qc.dependency_id,
                                question_configs,
                                answers
                            )
                            
                            with connection.cursor() as cursor:
                                cursor.execute(resolved_sql)
                                results = cursor.fetchall()
                                
                                # Convert results to list of dictionaries with valueField and textField
                                options = []
                                for row in results:
                                    if len(row) >= 2:  # Ensure we have at least two columns
                                        options.append({
                                            'valueField': str(row[0]),  # First column as value
                                            'textField': str(row[1])    # Second column as text
                                        })
                                question_data['question_options'] = options
                        except Exception as e:
                            log_error('configuration_list.execute_sql', e)
                            # Keep original question_options if SQL execution fails
                            pass
                    
                    questions.append(question_data)
                
                config_data = {
                    "header": config.header,
                    "icon": config.icon,
                    "color": config.color,
                    "questions": questions,
                    "is_quick_menu": config.is_quick_menu,
                    "function_ref_id": config.function_ref_id
                }
                
                # Apply translations
                config_data, questions = apply_translations(config_data, questions, user_language)
                config_data['questions'] = questions
                
                # Add to appropriate menu type
                if config.menu_type == 'InMenu':
                    response_data['InMenu'][config.function_ref_id] = config_data
                elif config.menu_type == 'OutMenu':
                    response_data['OutMenu'][config.function_ref_id] = config_data
                else:
                    # If no menu type specified, add directly to root
                    response_data[config.function_ref_id] = config_data
            
            return Response(response_data)
        except Exception as e:
            log_error('configuration_list.GET', e)
            return Response(
                {"error": "An error occurred while fetching configurations"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    elif request.method == 'POST':
        try:
            with transaction.atomic():
                serializer = MenuSerializer(data=request.data)
                if serializer.is_valid():
                    config = serializer.save()
                    response_serializer = MenuSerializer(config)
                    return Response(response_serializer.data, status=status.HTTP_201_CREATED)
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            log_error('configuration_list.POST', e)
            return Response(
                {"error": "An error occurred while creating configuration"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

@api_view(['GET', 'PUT', 'DELETE'])
def configuration_detail(request, pk):
    try:
        configuration = Configuration.objects.get(pk=pk)
    except Configuration.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    except Exception as e:
        log_error('configuration_detail.get_configuration', e)
        return Response(
            {"error": "An error occurred while fetching configuration"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )

    if request.method == 'GET':
        try:
            # Get user's language preference
            user_language = get_user_language(request)
            
            # Get all question configurations for this function_ref_id, ordered by order field
            question_configs = QuestionConfiguration.objects.filter(
                function_ref_id=configuration.function_ref_id
            ).order_by('order', 'created_at')
            
            # Get answers from query parameters (for dependency resolution)
            answers = {}
            if 'answers' in request.query_params:
                try:
                    answers = json.loads(request.query_params['answers'])
                except (json.JSONDecodeError, TypeError):
                    pass
            else:
                # Also check for individual answer parameters
                for key, value in request.query_params.items():
                    if key.startswith('answer_'):
                        answer_key = key.replace('answer_', '')
                        answers[answer_key] = value
            
            questions = []
            for qc in question_configs:
                question_data = {
                    "label": qc.label,
                    "read_only": qc.read_only,
                    "required": qc.required,
                    "question_type": qc.question_type,
                    "question_options": qc.question_options,
                    "border_color": qc.border_color,
                    "custom_sql": qc.custom_sql,
                    "order": qc.order,
                    "question_category": qc.question_category or 'None',
                    "question_id": qc.question_id or '',
                    "dependency_id": qc.dependency_id or ''
                }
                
                # If custom_sql is present, execute it and update question_options
                if qc.custom_sql:
                    try:
                        # Resolve dependency if present
                        resolved_sql = resolve_dependency_in_sql(
                            qc.custom_sql,
                            qc.dependency_id,
                            question_configs,
                            answers
                        )
                        
                        with connection.cursor() as cursor:
                            cursor.execute(resolved_sql)
                            results = cursor.fetchall()
                            
                            # Convert results to list of dictionaries with valueField and textField
                            options = []
                            for row in results:
                                if len(row) >= 2:  # Ensure we have at least two columns
                                    options.append({
                                        'valueField': str(row[0]),  # First column as value
                                        'textField': str(row[1])    # Second column as text
                                    })
                            question_data['question_options'] = options
                    except Exception as e:
                        log_error('configuration_detail.execute_sql', e)
                        # Keep original question_options if SQL execution fails
                        pass
                
                questions.append(question_data)
            
            config_data = {
                "header": configuration.header,
                "icon": configuration.icon,
                "color": configuration.color,
                "questions": questions,
                "is_quick_menu": configuration.is_quick_menu,
                "function_ref_id": configuration.function_ref_id
            }
            
            # Apply translations based on user's language
            config_data, questions = apply_translations(config_data, questions, user_language)
            config_data['questions'] = questions
            
            return Response(config_data)
        except Exception as e:
            log_error('configuration_detail.GET', e)
            return Response(
                {"error": "An error occurred while fetching configuration details"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    elif request.method == 'PUT':
        try:
            with transaction.atomic():
                serializer = MenuSerializer(configuration, data=request.data)
                if serializer.is_valid():
                    config = serializer.save()
                    response_serializer = MenuSerializer(config)
                    return Response(response_serializer.data)
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            log_error('configuration_detail.PUT', e)
            return Response(
                {"error": "An error occurred while updating configuration"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    elif request.method == 'DELETE':
        try:
            with transaction.atomic():
                configuration.delete()
                # Get the updated list of all configurations
                all_configs = Configuration.objects.all()
                serializer = MenuSerializer(all_configs)
                return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
        except Exception as e:
            log_error('configuration_detail.DELETE', e)
            return Response(
                {"error": "An error occurred while deleting configuration"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

@api_view(['GET'])
def get_configuration_by_function_ref(request, function_ref_id):
    try:
        # Get the configuration
        configuration = Configuration.objects.get(function_ref_id=function_ref_id)
        
        # Get user's language preference
        user_language = get_user_language(request)
        
        # Get all question configurations for this function_ref_id, ordered by order field
        question_configs = QuestionConfiguration.objects.filter(
            function_ref_id=function_ref_id
        ).order_by('order', 'created_at')
        
        # Get answers from query parameters (for dependency resolution)
        # Answers can be sent as JSON string in 'answers' param, or as individual params
        answers = {}
        if 'answers' in request.query_params:
            try:
                answers = json.loads(request.query_params['answers'])
            except (json.JSONDecodeError, TypeError):
                pass
        else:
            # Also check for individual answer parameters (label-based or question_id-based)
            for key, value in request.query_params.items():
                if key.startswith('answer_'):
                    answer_key = key.replace('answer_', '')
                    answers[answer_key] = value
        
        # Prepare the response data
        questions = []
        for qc in question_configs:
            question_data = {
                "label": qc.label,
                "read_only": qc.read_only,
                "required": qc.required,
                "question_type": qc.question_type,
                "question_options": qc.question_options,
                "border_color": qc.border_color,
                "custom_sql": qc.custom_sql,
                "order": qc.order,
                "question_category": qc.question_category or 'None',
                "question_id": qc.question_id or '',
                "dependency_id": qc.dependency_id or ''
            }
            
            # If custom_sql is present, execute it and update question_options
            if qc.custom_sql:
                try:
                    # Resolve dependency if present
                    resolved_sql = resolve_dependency_in_sql(
                        qc.custom_sql,
                        qc.dependency_id,
                        question_configs,
                        answers
                    )
                    
                    with connection.cursor() as cursor:
                        cursor.execute(resolved_sql)
                        results = cursor.fetchall()
                        
                        # Convert results to list of dictionaries with valueField and textField
                        options = []
                        for row in results:
                            if len(row) >= 2:  # Ensure we have at least two columns
                                options.append({
                                    'valueField': str(row[0]),  # First column as value
                                    'textField': str(row[1])    # Second column as text
                                })
                        question_data['question_options'] = options
                except Exception as e:
                    log_error('get_configuration_by_function_ref.execute_sql', e)
                    # Keep original question_options if SQL execution fails
                    pass
            
            questions.append(question_data)
        
        config_data = {
            "header": configuration.header,
            "icon": configuration.icon,
            "color": configuration.color,
            "questions": questions,
            "is_quick_menu": configuration.is_quick_menu,
            "function_ref_id": configuration.function_ref_id
        }
        
        # Apply translations
        config_data, questions = apply_translations(config_data, questions, user_language)
        config_data['questions'] = questions
        
        response_data = {
            function_ref_id: config_data
        }
        
        return Response(response_data)
        
    except Configuration.DoesNotExist:
        return Response(
            {"error": f"Configuration not found for function_ref_id: {function_ref_id}"},
            status=status.HTTP_404_NOT_FOUND
        )
    except Exception as e:
        log_error('get_configuration_by_function_ref', e)
        return Response(
            {"error": "An error occurred while fetching configuration"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )

@api_view(['DELETE'])
def delete_configuration_by_function_ref(request, function_ref_id):
    try:
        with transaction.atomic():
            # Get all configurations with the given function_ref_id
            configurations = Configuration.objects.filter(function_ref_id=function_ref_id)
            
            if not configurations.exists():
                return Response(
                    {"error": f"No configurations found for function_ref_id: {function_ref_id}"},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            # Delete all matching configurations
            configurations.delete()
            
            return Response(
                {"message": f"Successfully deleted all configurations for function_ref_id: {function_ref_id}"},
                status=status.HTTP_200_OK
            )
            
    except Exception as e:
        log_error('delete_configuration_by_function_ref', e)
        return Response(
            {"error": "An error occurred while deleting configurations"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated])
def app_settings(request):
    """
    Get or update application settings (SMTP configuration)
    """
    try:
        settings = AppSettings.get_settings()
        
        if request.method == 'GET':
            serializer = AppSettingsSerializer(settings)
            return Response(serializer.data)
        
        elif request.method == 'PUT':
            serializer = AppSettingsSerializer(settings, data=request.data, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
            
    except Exception as e:
        log_error('app_settings', e)
        return Response(
            {"error": "An error occurred while processing app settings"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        ) 