
import traceback

import jinja2
from flask import render_template, request, jsonify, redirect, current_app, flash
from flask_login import current_user

from common.utils.eveai_exceptions import EveAINoSessionTenant
from common.utils.nginx_utils import prefixed_url_for


def not_found_error(error):
    profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
    if profile == 'web_app':
        if not current_user.is_authenticated:
            return redirect(prefixed_url_for('security.login', for_redirect=True))
    current_app.logger.error(f"Not Found Error: {error}")
    current_app.logger.error(traceback.format_exc())
    return render_template('error/404.html'), 404


def internal_server_error(error):
    profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
    if profile == 'web_app':
        if not current_user.is_authenticated:
            return redirect(prefixed_url_for('security.login', for_redirect=True))
    current_app.logger.error(f"Internal Server Error: {error}")
    current_app.logger.error(traceback.format_exc())
    return render_template('error/500.html'), 500


def not_authorised_error(error):
    profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
    if profile == 'web_app':
        if not current_user.is_authenticated:
            return redirect(prefixed_url_for('security.login', for_redirect=True))
    current_app.logger.error(f"Not Authorised Error: {error}")
    current_app.logger.error(traceback.format_exc())
    return render_template('error/401.html'), 401


def access_forbidden(error):
    profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
    if profile == 'web_app':
        if not current_user.is_authenticated:
            return redirect(prefixed_url_for('security.login', for_redirect=True))
    current_app.logger.error(f"Access Forbidden: {error}")
    current_app.logger.error(traceback.format_exc())
    return render_template('error/403.html'), 403


def key_error_handler(error):
    profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
    # Check if the KeyError is specifically for 'tenant'
    if str(error) == "'tenant'":
        if profile == 'web_app':
            return redirect(prefixed_url_for('security.login', for_redirect=True))
        else:
            current_app.logger.warning("Session tenant missing in chat_client context")
            return render_template('error/401.html'), 401
    # For other KeyErrors, you might want to log the error and return a generic error page
    current_app.logger.error(f"Key Error: {error}")
    current_app.logger.error(traceback.format_exc())
    return render_template('error/generic.html', error_message="An unexpected error occurred"), 500


def attribute_error_handler(error):
    """Handle AttributeError exceptions.

            Specifically catches SQLAlchemy relationship errors when string IDs
            are used instead of model instances.
            """
    error_msg = str(error)
    current_app.logger.error(f"AttributeError: {error_msg}")
    current_app.logger.error(traceback.format_exc())

    # Handle the SQLAlchemy relationship error specifically
    if "'str' object has no attribute '_sa_instance_state'" in error_msg:
        flash('Database relationship error. Please check your form inputs and try again.', 'error')
        return render_template('error/500.html',
                               error_type="Relationship Error",
                               error_details="A string value was provided where a database object was expected."), 500

    # Handle other AttributeErrors
    flash('An application error occurred. The technical team has been notified.', 'error')
    return render_template('error/500.html',
                           error_type="Attribute Error",
                           error_details=error_msg), 500


def no_tenant_selected_error(error):
    """Handle errors when no tenant is selected in the current session.

    This typically happens when a session expires or becomes invalid after
    a long period of inactivity. The user will be redirected to the login page (web_app)
    or shown an error page (chat_client).
    """
    profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
    current_app.logger.error(f"No Session Tenant Error: {error}")
    current_app.logger.error(traceback.format_exc())
    flash('Your session expired. You will have to re-enter your credentials', 'warning')

    if profile == 'web_app':
        # Perform logout if user is authenticated
        if current_user.is_authenticated:
            from flask_security.utils import logout_user
            logout_user()
        # Redirect to login page
        return redirect(prefixed_url_for('security.login', for_redirect=True))
    else:
        # chat_client: render 401 page
        return render_template('error/401.html'), 401


def general_exception(e):
    current_app.logger.error(f"Unhandled Exception: {e}", exc_info=True)
    flash('An application error occurred. The technical team has been notified.', 'error')
    return render_template('error/500.html',
                           error_type=type(e).__name__,
                           error_details=str(e)), 500


def template_not_found_error(error):
    """Handle Jinja2 TemplateNotFound exceptions."""
    current_app.logger.error(f'Template not found: {error.name}')
    current_app.logger.error(f'Search Paths: {current_app.jinja_loader.list_templates()}')
    current_app.logger.error(traceback.format_exc())
    return render_template('error/500.html',
                          error_type="Template Not Found",
                          error_details=f"Template '{error.name}' could not be found."), 404


def template_syntax_error(error):
    """Handle Jinja2 TemplateSyntaxError exceptions."""
    current_app.logger.error(f'Template syntax error: {error.message}')
    current_app.logger.error(f'In template {error.filename}, line {error.lineno}')
    current_app.logger.error(traceback.format_exc())
    return render_template('error/500.html',
                          error_type="Template Syntax Error",
                          error_details=f"Error in template '{error.filename}' at line {error.lineno}: {error.message}"), 500


def register_error_handlers(app, profile: str = 'web_app'):
    # Store profile in app config to drive handler behavior
    app.config['ERRORS_PROFILE'] = profile
    
    app.register_error_handler(404, not_found_error)
    app.register_error_handler(500, internal_server_error)
    app.register_error_handler(401, not_authorised_error)
    app.register_error_handler(403, not_authorised_error)
    app.register_error_handler(EveAINoSessionTenant, no_tenant_selected_error)
    app.register_error_handler(KeyError, key_error_handler)
    app.register_error_handler(AttributeError, attribute_error_handler)
    app.register_error_handler(jinja2.TemplateNotFound, template_not_found_error)
    app.register_error_handler(jinja2.TemplateSyntaxError, template_syntax_error)
    app.register_error_handler(Exception, general_exception)