from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
from flask_login import login_required, current_user
from auth.models import User, Company, UserCompany
from api.models import Log
from extensions import db
from datetime import datetime, timedelta
from sqlalchemy import or_
import pytz
import logging

# Initialize logger for this module
logger = logging.getLogger(__name__)

frontend = Blueprint('frontend', __name__)

frontend_bp = frontend

@frontend.route('/')
@frontend.route('/home')
def index():
    try:
        return render_template('frontend/home.html')
    except Exception as e:
        logger.exception("Home page error from IP %s: %s", request.remote_addr, str(e))
        flash('An error occurred while loading the home page.', 'error')
        return render_template('frontend/home.html')  # Fallback to basic template

@frontend.route('/about')
def about():
    try:
        return render_template('frontend/about.html')
    except Exception as e:
        logger.exception("About page error from IP %s: %s", request.remote_addr, str(e))
        flash('An error occurred while loading the about page.', 'error')
        return render_template('frontend/about.html')  # Fallback to basic template

@frontend.route('/profile')
@login_required
def profile():
    try:
        user = User.query.filter_by(id=current_user.id).first()
        return render_template('frontend/profile.html', user=user)
    except Exception as e:
        logger.exception(
            "Profile page error for user %s (ID: %s) from IP %s: %s",
            current_user.username if current_user.is_authenticated else 'anonymous',
            current_user.id if current_user.is_authenticated else 'N/A',
            request.remote_addr,
            str(e)
        )
        flash('An error occurred while loading your profile. Please try again.', 'error')
        return redirect(url_for('frontend.index'))

@frontend.route('/dashboard')
@login_required
def dashboard():
    try:
        # Get app timezone
        app_tz = pytz.timezone(current_app.config['TIMEZONE'])
        
        # Get date range from request or default to last 48 hours
        date_range = request.args.get('daterange')
        
        if date_range:
            start_str, end_str = date_range.split(' - ')
            start_date = datetime.strptime(start_str, '%Y-%m-%d %H:%M')
            end_date = datetime.strptime(end_str, '%Y-%m-%d %H:%M')
            
            # Make these timezone aware using the app's timezone
            start_date = app_tz.localize(start_date)
            end_date = app_tz.localize(end_date)
        else:
            end_date = datetime.now(app_tz) + timedelta(hours=1)  # Include 1 hour in the future
            start_date = end_date - timedelta(hours=49)  # Look back 48 hours from future end time

        # Get company filter if provided
        company_id = request.args.get('company_id', type=int)
        
        # Build base query with date range filter and eagerly load relationships
        from auth.models import ApiKey, Company
        
        # Convert timezone-aware dates to naive for comparison with database timestamps
        # since the log timestamps are stored as naive datetime objects
        start_date_naive = start_date.replace(tzinfo=None)
        end_date_naive = end_date.replace(tzinfo=None)
        
        # Use joinedload to eagerly load the API key and company relationships
        query = Log.query.options(
            db.joinedload(Log.api_key),
            db.joinedload(Log.company)
        ).filter(Log.timestamp.between(start_date_naive, end_date_naive))
        
        # Apply company-specific filtering based on user role
        if current_user.is_global_admin():
            # GlobalAdmin should be allowed to see all records, no matter what company/site
            if company_id:
                query = query.filter(Log.company_id == company_id)
            # If no company_id specified, show all logs (no additional filtering)
        else:
            # CompanyAdmin and User should see only company log events and API Keys (sites) 
            # for companies they are member of
            user_company_ids = [uc.company_id for uc in current_user.companies]
            
            if not user_company_ids:
                # If user has no company associations, show no logs
                query = query.filter(Log.id == -1)  # Impossible condition = no results
            else:
                if company_id and company_id in user_company_ids:
                    # Filter by the specific company if requested and user has access
                    query = query.filter(Log.company_id == company_id)
                else:
                    # Show logs from all companies the user has access to
                    query = query.filter(Log.company_id.in_(user_company_ids))
        
        # Get the logs ordered by timestamp (newest first)
        logs = query.order_by(Log.timestamp.desc()).all()
        
        # Get companies for the dropdown filter based on user role
        if current_user.is_global_admin():
            # GlobalAdmin can see all companies
            companies = Company.query.all()
        else:
            # CompanyAdmin and User should see only companies they are member of
            companies = current_user.get_companies()

        return render_template('frontend/dashboard.html', 
                              title='Dashboard', 
                              logs=logs,
                              start_date=start_date,
                              end_date=end_date,
                              companies=companies,
                              selected_company_id=company_id)
    
    except Exception as e:
        logger.exception(
            "Dashboard error for user %s (ID: %s, role: %s) from IP %s: %s",
            current_user.username if current_user.is_authenticated else 'anonymous',
            current_user.id if current_user.is_authenticated else 'N/A',
            current_user.role if current_user.is_authenticated else 'N/A',
            request.remote_addr,
            str(e)
        )
        flash('An error occurred while loading the dashboard. Please try again.', 'error')
        return redirect(url_for('frontend.index'))

@frontend.route('/time_spent_report')
@login_required
def time_spent_report():
    try:
        # Get app timezone
        app_tz = pytz.timezone(current_app.config['TIMEZONE'])
        
        # Get date range from request or default to last 7 days
        date_range = request.args.get('daterange')
        
        if date_range:
            start_str, end_str = date_range.split(' - ')
            start_date = datetime.strptime(start_str, '%Y-%m-%d %H:%M')
            end_date = datetime.strptime(end_str, '%Y-%m-%d %H:%M')
            
            # Make these timezone aware using the app's timezone
            start_date = app_tz.localize(start_date)
            end_date = app_tz.localize(end_date)
        else:
            end_date = datetime.now(app_tz) + timedelta(hours=1)  # Include 1 hour in the future
            start_date = end_date - timedelta(days=7, hours=1)  # Look back 7 days from future end time

        # Get filters
        company_id = request.args.get('company_id', type=int)
        api_key_id = request.args.get('api_key_id', type=int)
        group_by = request.args.get('group_by', 'user')  # Default to user grouping
        continue_iterate = request.args.get('continue_iterate') == 'on'  # Get checkbox value
        
        # Build base query with date range filter
        from auth.models import ApiKey, Company
        
        # Start with all the login/logout events within the date range
        # Convert timezone-aware dates to naive for comparison with database timestamps
        start_date_naive = start_date.replace(tzinfo=None)
        end_date_naive = end_date.replace(tzinfo=None)        
        logs_query = Log.query.filter(
            Log.timestamp.between(start_date_naive, end_date_naive)
        ).order_by(Log.timestamp.asc())
        
        # Apply company-specific filtering based on user role
        if current_user.is_global_admin():
            # GlobalAdmin should be allowed to see all records, no matter what company/site
            if company_id:
                logs_query = logs_query.filter(Log.company_id == company_id)
            # If no company_id specified, show all logs (no additional filtering)
        else:
            # CompanyAdmin and User should see only company log events for companies they are member of
            user_company_ids = [uc.company_id for uc in current_user.companies]
            
            if not user_company_ids:
                # If user has no company associations, show no logs
                logs_query = logs_query.filter(Log.id == -1)  # Impossible condition = no results
            else:
                if company_id and company_id in user_company_ids:
                    # Filter by the specific company if requested and user has access
                    logs_query = logs_query.filter(Log.company_id == company_id)
                else:
                    # Show logs from all companies the user has access to
                    logs_query = logs_query.filter(Log.company_id.in_(user_company_ids))
        
        # Apply API key filter if provided
        if api_key_id:
            # Ensure the API key belongs to a company the user has access to
            api_key = ApiKey.query.get(api_key_id)
            if api_key:
                if current_user.is_global_admin():
                    # GlobalAdmin can use any API key
                    logs_query = logs_query.filter(Log.api_key_id == api_key_id)
                else:
                    # Check if the API key belongs to a company the user has access to
                    user_company_ids = [uc.company_id for uc in current_user.companies]
                    if api_key.company_id in user_company_ids:
                        logs_query = logs_query.filter(Log.api_key_id == api_key_id)
                    # If API key doesn't belong to user's company, ignore the filter
        
        # Get all the relevant logs
        logs = logs_query.all()
        
        # Process logs to calculate time spent
        time_data = calculate_time_spent(logs, group_by, continue_iterate)
        
        # Get all companies for the dropdown filter
        if current_user.is_global_admin():
            companies = Company.query.all()
        else:
            companies = current_user.get_companies()
        
        # Get available API keys for filter
        if current_user.is_global_admin():
            api_keys = ApiKey.query.all()
        else:
            # Get API keys for companies user has access to
            user_company_ids = [uc.company_id for uc in current_user.companies]
            api_keys = ApiKey.query.filter(ApiKey.company_id.in_(user_company_ids)).all()

        return render_template('frontend/time_spent_report.html', 
                              title='Time Spent Report', 
                              time_data=time_data,
                              start_date=start_date,
                              end_date=end_date,
                              companies=companies,
                              api_keys=api_keys,
                              selected_company_id=company_id,
                              selected_api_key_id=api_key_id,
                              group_by=group_by,
                              continue_iterate=continue_iterate)
    
    except Exception as e:
        logger.exception(
            "Time spent report error for user %s (ID: %s, role: %s) from IP %s: %s",
            current_user.username if current_user.is_authenticated else 'anonymous',
            current_user.id if current_user.is_authenticated else 'N/A',
            current_user.role if current_user.is_authenticated else 'N/A',
            request.remote_addr,
            str(e)
        )
        flash('An error occurred while generating the time spent report. Please try again.', 'error')
        return redirect(url_for('frontend.dashboard'))

def calculate_time_spent(logs, group_by='user', continue_iterate=False):
    """
    Calculate time spent by users based on login/logout events.
    
    Args:
        logs: List of Log objects sorted by timestamp
        group_by: Whether to group by 'user' or 'user_computer'
        continue_iterate: Whether to continue iterating for additional login/logout pairs
        
    Returns:
        List of dictionaries with time spent information
    """
    from auth.models import Company, ApiKey
    
    # Dictionary to track user sessions
    # Key: user_name or user_name+computer_name depending on group_by
    # Value: dictionary with session tracking info
    active_sessions = {}
    
    # Dictionary to accumulate total time spent
    # Key: date + user_name + (computer_name) + company_id
    # Value: dictionary with accumulated time and session details
    time_totals = {}
    
    # Define login and logout event types - case-insensitive matching
    login_events = ['login', 'unlock', 'logon']  # Events that start a session
    logout_events = ['logout', 'lock', 'logoff']  # Events that end a session
    
    # Create a set to track which users have appeared in logs
    seen_users = set()
    
    # First pass: populate the time_totals dictionary with user entries
    # This ensures every user has an entry even if they don't have paired login/logout events
    for log in logs:
        # Determine the session key based on grouping option
        session_key = log.user_name
        if group_by == 'user_computer':
            session_key = f"{log.user_name}:{log.computer_name}"
        
        # Get date string in format YYYY-MM-DD
        log_date = log.timestamp.strftime('%Y-%m-%d')
        
        # Create a unique key for the time totals
        total_key = f"{log_date}:{session_key}:{log.company_id}"
        
        # Initialize the time total entry if it doesn't exist
        if total_key not in time_totals:
            # Get company info
            company = Company.query.get(log.company_id) if log.company_id else None
            company_name = company.name if company else "Unknown"
            
            # Get API key info
            api_key = ApiKey.query.get(log.api_key_id) if log.api_key_id else None
            api_key_description = api_key.description if api_key else "Unknown"
            
            # Initialize with zero time
            time_totals[total_key] = {
                'date': log_date,
                'user_name': log.user_name,
                'computer_name': log.computer_name if group_by == 'user_computer' else None,
                'company_id': log.company_id,
                'company_name': company_name,
                'api_key_id': log.api_key_id,
                'api_key_description': api_key_description,
                'total_seconds': 0,
                'first_login': None,
                'last_logout': None,
                'session_count': 0
            }
            
            # Track that we've seen this user
            seen_users.add(log.user_name)
            
        # Update login/logout timestamps even if we can't calculate duration
        event_type = log.event_type.lower() if log.event_type else ''
        
        # For all users, record their first login and last logout
        if event_type in login_events:
            if not time_totals[total_key]['first_login'] or log.timestamp < time_totals[total_key]['first_login']:
                time_totals[total_key]['first_login'] = log.timestamp
        
        if event_type in logout_events:
            if not time_totals[total_key]['last_logout'] or log.timestamp > time_totals[total_key]['last_logout']:
                time_totals[total_key]['last_logout'] = log.timestamp
    
    # Second pass: calculate session durations
    for log_index, log in enumerate(logs):
        # Determine the session key based on grouping option
        session_key = log.user_name
        if group_by == 'user_computer':
            session_key = f"{log.user_name}:{log.computer_name}"
        
        # Get date string in format YYYY-MM-DD
        log_date = log.timestamp.strftime('%Y-%m-%d')
        
        # Create a unique key for the time totals
        total_key = f"{log_date}:{session_key}:{log.company_id}"
        
        # Convert event_type to lowercase for case-insensitive comparison
        event_type = log.event_type.lower() if log.event_type else ''
        
        # Process any login-like event
        if event_type in login_events:
            # Record login time for this session
            active_sessions[session_key] = {
                'login_time': log.timestamp,
                'log_index': log_index,
                'company_id': log.company_id,
                'date': log_date
            }
        
        # Process any logout-like event
        elif event_type in logout_events and session_key in active_sessions:
            session = active_sessions[session_key]
            
            # Only process if session is from the same day and company
            if session['date'] == log_date and session['company_id'] == log.company_id:
                # Calculate duration of this session
                duration = (log.timestamp - session['login_time']).total_seconds()
                
                # Only count if duration is positive and reasonable (< 24 hours)
                if 0 < duration < 86400:  # 24 hours = 86400 seconds
                    # Add to the total time for this user/day combination
                    time_totals[total_key]['total_seconds'] += duration
                    time_totals[total_key]['session_count'] += 1
                
                # If we should continue iterating, leave the session active to match with future login events
                # Otherwise, remove the active session after processing
                if not continue_iterate:
                    del active_sessions[session_key]
            else:
                # If the session doesn't match day/company, remove it if we're not continuing to iterate
                if not continue_iterate:
                    del active_sessions[session_key]
    
    # Special case: If we only have one event of each type per user per day,
    # calculate duration between first login and last logout
    for total_key, entry in time_totals.items():
        if entry['session_count'] == 0 and entry['first_login'] and entry['last_logout']:
            # Calculate duration between first login and last logout
            duration = (entry['last_logout'] - entry['first_login']).total_seconds()
            
            # Only use if duration is positive and reasonable
            if 0 < duration < 86400:  # 24 hours = 86400 seconds
                entry['total_seconds'] += duration
                entry['session_count'] = 1
    
    # Convert the time_totals dictionary to a list of dictionaries
    result = []
    for entry in time_totals.values():
        # Format the total time as hours:minutes:seconds
        hours, remainder = divmod(entry['total_seconds'], 3600)
        minutes, seconds = divmod(remainder, 60)
        entry['formatted_time'] = f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}"
        
        result.append(entry)
    
    # Sort by date (newest first) and then by user_name
    result.sort(key=lambda x: (x['date'], x['user_name']), reverse=True)
    
    return result