import logging
from datetime import datetime
from flask import request, jsonify, current_app
from flask_jwt_extended import jwt_required, get_jwt_identity
from sqlalchemy import func, desc
from app.api import bp
from app import db
from app.utils.security import require_admin, require_active_user
from app.utils.logging import log_api_request
from app.upload.file_handler import get_file_upload_handler
from app.upload.fixture_parser import get_fixture_parser

logger = logging.getLogger(__name__)

@bp.route('/matches', methods=['GET'])
@jwt_required()
def api_get_matches():
    """Get matches with pagination and filtering"""
    try:
        from app.models import User, Match, FileUpload
        user_id = get_jwt_identity()
        user = User.query.get(user_id)
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        # Pagination parameters
        page = request.args.get('page', 1, type=int)
        per_page = min(request.args.get('per_page', 20, type=int), 100)
        
        # Filtering parameters
        status_filter = request.args.get('status')
        search_query = request.args.get('search', '').strip()
        active_only = request.args.get('active_only', 'false').lower() == 'true'
        
        # Base query
        if user.is_admin:
            query = Match.query
        else:
            query = Match.query.filter_by(created_by=user_id)
        
        # Apply filters
        if active_only:
            query = query.filter_by(active_status=True)
        
        if status_filter == 'active':
            query = query.filter_by(active_status=True)
        elif status_filter == 'pending':
            query = query.filter_by(active_status=False)
        elif status_filter == 'zip_pending':
            query = query.filter_by(zip_upload_status='pending')
        elif status_filter == 'zip_completed':
            query = query.filter_by(zip_upload_status='completed')
        elif status_filter == 'zip_failed':
            query = query.filter_by(zip_upload_status='failed')
        
        # Search functionality
        if search_query:
            search_pattern = f"%{search_query}%"
            query = query.filter(
                db.or_(
                    Match.fighter1_township.ilike(search_pattern),
                    Match.fighter2_township.ilike(search_pattern),
                    Match.venue_kampala_township.ilike(search_pattern),
                    Match.match_number.like(search_pattern)
                )
            )
        
        # Sorting
        sort_by = request.args.get('sort_by', 'created_at')
        sort_order = request.args.get('sort_order', 'desc')
        
        if hasattr(Match, sort_by):
            sort_column = getattr(Match, sort_by)
            if sort_order == 'asc':
                query = query.order_by(sort_column.asc())
            else:
                query = query.order_by(sort_column.desc())
        else:
            query = query.order_by(Match.created_at.desc())
        
        # Execute pagination
        matches_pagination = query.paginate(
            page=page, per_page=per_page, error_out=False
        )
        
        # Include outcomes if requested
        include_outcomes = request.args.get('include_outcomes', 'false').lower() == 'true'
        
        matches_data = []
        for match in matches_pagination.items:
            match_dict = match.to_dict(include_outcomes=include_outcomes)
            matches_data.append(match_dict)
        
        return jsonify({
            'matches': matches_data,
            'pagination': {
                'page': page,
                'pages': matches_pagination.pages,
                'per_page': per_page,
                'total': matches_pagination.total,
                'has_next': matches_pagination.has_next,
                'has_prev': matches_pagination.has_prev
            },
            'filters': {
                'status': status_filter,
                'search': search_query,
                'active_only': active_only,
                'sort_by': sort_by,
                'sort_order': sort_order
            }
        }), 200
        
    except Exception as e:
        logger.error(f"API get matches error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve matches'}), 500

@bp.route('/matches/<int:match_id>', methods=['GET'])
@jwt_required()
def api_get_match(match_id):
    """Get specific match details"""
    try:
        from app.models import User, Match, FileUpload
        user_id = get_jwt_identity()
        user = User.query.get(user_id)
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        # Get match
        if user.is_admin:
            match = Match.query.get(match_id)
        else:
            match = Match.query.filter_by(id=match_id, created_by=user_id).first()
        
        if not match:
            return jsonify({'error': 'Match not found'}), 404
        
        # Get associated uploads
        uploads = FileUpload.query.filter_by(match_id=match_id).all()
        
        return jsonify({
            'match': match.to_dict(include_outcomes=True),
            'uploads': [upload.to_dict() for upload in uploads]
        }), 200
        
    except Exception as e:
        logger.error(f"API get match error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve match'}), 500

@bp.route('/matches/<int:match_id>', methods=['PUT'])
@jwt_required()
def api_update_match(match_id):
    """Update match details"""
    try:
        from app.models import User, Match
        user_id = get_jwt_identity()
        user = User.query.get(user_id)
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        # Get match
        if user.is_admin:
            match = Match.query.get(match_id)
        else:
            match = Match.query.filter_by(id=match_id, created_by=user_id).first()
        
        if not match:
            return jsonify({'error': 'Match not found'}), 404
        
        data = request.get_json()
        if not data:
            return jsonify({'error': 'No data provided'}), 400
        
        # Update allowed fields
        updatable_fields = ['start_time', 'end_time', 'result']
        updated_fields = []
        
        for field in updatable_fields:
            if field in data:
                if field in ['start_time', 'end_time'] and data[field]:
                    try:
                        setattr(match, field, datetime.fromisoformat(data[field]))
                        updated_fields.append(field)
                    except ValueError:
                        return jsonify({'error': f'Invalid datetime format for {field}'}), 400
                else:
                    setattr(match, field, data[field])
                    updated_fields.append(field)
        
        if updated_fields:
            match.updated_at = datetime.utcnow()
            db.session.commit()
            
            return jsonify({
                'message': 'Match updated successfully',
                'updated_fields': updated_fields,
                'match': match.to_dict()
            }), 200
        else:
            return jsonify({'message': 'No fields to update'}), 200
        
    except Exception as e:
        logger.error(f"API update match error: {str(e)}")
        db.session.rollback()
        return jsonify({'error': 'Failed to update match'}), 500

@bp.route('/matches/<int:match_id>', methods=['DELETE'])
@jwt_required()
def api_delete_match(match_id):
    """Delete match (admin only)"""
    try:
        from app.models import User, Match, FileUpload
        user_id = get_jwt_identity()
        user = User.query.get(user_id)
        
        if not user or not user.is_admin:
            return jsonify({'error': 'Admin privileges required'}), 403
        
        match = Match.query.get(match_id)
        if not match:
            return jsonify({'error': 'Match not found'}), 404
        
        # Delete associated files
        uploads = FileUpload.query.filter_by(match_id=match_id).all()
        for upload in uploads:
            try:
                import os
                if os.path.exists(upload.file_path):
                    os.remove(upload.file_path)
            except Exception as e:
                logger.warning(f"Failed to delete file {upload.file_path}: {str(e)}")
        
        # Delete match (cascades to outcomes and uploads)
        db.session.delete(match)
        db.session.commit()
        
        return jsonify({'message': 'Match deleted successfully'}), 200
        
    except Exception as e:
        logger.error(f"API delete match error: {str(e)}")
        db.session.rollback()
        return jsonify({'error': 'Failed to delete match'}), 500

@bp.route('/statistics', methods=['GET'])
@jwt_required()
def api_get_statistics():
    """Get comprehensive statistics"""
    try:
        from app.models import User, Match, FileUpload
        user_id = get_jwt_identity()
        user = User.query.get(user_id)
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        # User statistics
        user_stats = {
            'total_matches': Match.query.filter_by(created_by=user_id).count(),
            'active_matches': Match.query.filter_by(
                created_by=user_id, active_status=True
            ).count(),
            'total_uploads': FileUpload.query.filter_by(uploaded_by=user_id).count(),
            'completed_uploads': FileUpload.query.filter_by(
                uploaded_by=user_id, upload_status='completed'
            ).count(),
            'failed_uploads': FileUpload.query.filter_by(
                uploaded_by=user_id, upload_status='failed'
            ).count(),
            'pending_zip_uploads': Match.query.filter_by(
                created_by=user_id, zip_upload_status='pending'
            ).count()
        }
        
        # Global statistics (if admin)
        global_stats = {}
        if user.is_admin:
            from app.database import get_db_manager
            db_manager = get_db_manager()
            global_stats = db_manager.get_database_stats()
            
            file_handler = get_file_upload_handler()
            fixture_parser = get_fixture_parser()
            
            upload_stats = file_handler.get_upload_statistics()
            parsing_stats = fixture_parser.get_parsing_statistics()
            
            global_stats.update({
                'upload_stats': upload_stats,
                'parsing_stats': parsing_stats
            })
        
        # Recent activity
        recent_matches = Match.query.filter_by(created_by=user_id)\
            .order_by(Match.created_at.desc()).limit(5).all()
        
        recent_uploads = FileUpload.query.filter_by(uploaded_by=user_id)\
            .order_by(FileUpload.created_at.desc()).limit(5).all()
        
        return jsonify({
            'user_stats': user_stats,
            'global_stats': global_stats,
            'recent_activity': {
                'matches': [match.to_dict(include_outcomes=False) for match in recent_matches],
                'uploads': [upload.to_dict() for upload in recent_uploads]
            }
        }), 200
        
    except Exception as e:
        logger.error(f"API statistics error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve statistics'}), 500

@bp.route('/admin/users', methods=['GET'])
@jwt_required()
@require_admin
def api_admin_get_users():
    """Get users list (admin only)"""
    try:
        page = request.args.get('page', 1, type=int)
        per_page = min(request.args.get('per_page', 20, type=int), 100)
        
        search_query = request.args.get('search', '').strip()
        status_filter = request.args.get('status')
        
        from app.models import User
        # Base query
        query = User.query
        
        # Apply filters
        if status_filter == 'active':
            query = query.filter_by(is_active=True)
        elif status_filter == 'inactive':
            query = query.filter_by(is_active=False)
        elif status_filter == 'admin':
            query = query.filter_by(is_admin=True)
        
        # Search functionality
        if search_query:
            search_pattern = f"%{search_query}%"
            query = query.filter(
                db.or_(
                    User.username.ilike(search_pattern),
                    User.email.ilike(search_pattern)
                )
            )
        
        # Pagination
        users_pagination = query.order_by(User.created_at.desc()).paginate(
            page=page, per_page=per_page, error_out=False
        )
        
        return jsonify({
            'users': [user.to_dict() for user in users_pagination.items],
            'pagination': {
                'page': page,
                'pages': users_pagination.pages,
                'per_page': per_page,
                'total': users_pagination.total,
                'has_next': users_pagination.has_next,
                'has_prev': users_pagination.has_prev
            }
        }), 200
        
    except Exception as e:
        logger.error(f"API admin users error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve users'}), 500

@bp.route('/admin/users/<int:user_id>', methods=['PUT'])
@jwt_required()
@require_admin
def api_admin_update_user(user_id):
    """Update user (admin only)"""
    try:
        from app.models import User
        user = User.query.get(user_id)
        if not user:
            return jsonify({'error': 'User not found'}), 404
        
        data = request.get_json()
        if not data:
            return jsonify({'error': 'No data provided'}), 400
        
        # Update allowed fields
        updatable_fields = ['is_active', 'is_admin']
        updated_fields = []
        
        for field in updatable_fields:
            if field in data:
                setattr(user, field, bool(data[field]))
                updated_fields.append(field)
        
        if updated_fields:
            user.updated_at = datetime.utcnow()
            db.session.commit()
            
            return jsonify({
                'message': 'User updated successfully',
                'updated_fields': updated_fields,
                'user': user.to_dict()
            }), 200
        else:
            return jsonify({'message': 'No fields to update'}), 200
        
    except Exception as e:
        logger.error(f"API admin update user error: {str(e)}")
        db.session.rollback()
        return jsonify({'error': 'Failed to update user'}), 500

@bp.route('/admin/logs', methods=['GET'])
@jwt_required()
@require_admin
def api_admin_get_logs():
    """Get system logs (admin only)"""
    try:
        page = request.args.get('page', 1, type=int)
        per_page = min(request.args.get('per_page', 50, type=int), 200)
        
        level_filter = request.args.get('level')
        module_filter = request.args.get('module')
        
        from app.models import SystemLog
        # Base query
        query = SystemLog.query
        
        # Apply filters
        if level_filter:
            query = query.filter_by(level=level_filter)
        
        if module_filter:
            query = query.filter_by(module=module_filter)
        
        # Pagination
        logs_pagination = query.order_by(SystemLog.created_at.desc()).paginate(
            page=page, per_page=per_page, error_out=False
        )
        
        return jsonify({
            'logs': [log.to_dict() for log in logs_pagination.items],
            'pagination': {
                'page': page,
                'pages': logs_pagination.pages,
                'per_page': per_page,
                'total': logs_pagination.total,
                'has_next': logs_pagination.has_next,
                'has_prev': logs_pagination.has_prev
            }
        }), 200
        
    except Exception as e:
        logger.error(f"API admin logs error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve logs'}), 500

@bp.route('/admin/system-info', methods=['GET'])
@jwt_required()
@require_admin
def api_admin_system_info():
    """Get system information (admin only)"""
    try:
        from app.database import get_db_manager
        db_manager = get_db_manager()
        
        # Database statistics
        db_stats = db_manager.get_database_stats()
        
        # System health
        db_healthy = db_manager.test_connection()
        
        # Upload statistics
        file_handler = get_file_upload_handler()
        upload_stats = file_handler.get_upload_statistics()
        
        # Parsing statistics
        fixture_parser = get_fixture_parser()
        parsing_stats = fixture_parser.get_parsing_statistics()
        
        from app.models import UserSession
        # Active sessions
        active_sessions = UserSession.query.filter_by(is_active=True).count()
        
        system_info = {
            'database': {
                'healthy': db_healthy,
                'statistics': db_stats
            },
            'uploads': upload_stats,
            'parsing': parsing_stats,
            'sessions': {
                'active_sessions': active_sessions
            },
            'timestamp': datetime.utcnow().isoformat()
        }
        
        return jsonify(system_info), 200
        
    except Exception as e:
        logger.error(f"API system info error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve system information'}), 500

@bp.route('/admin/cleanup', methods=['POST'])
@jwt_required()
@require_admin
def api_admin_cleanup():
    """Perform system cleanup (admin only)"""
    try:
        cleanup_type = request.json.get('type', 'all') if request.json else 'all'
        
        results = {}
        
        if cleanup_type in ['all', 'sessions']:
            # Clean up expired sessions
            from app.database import get_db_manager
            db_manager = get_db_manager()
            expired_sessions = db_manager.cleanup_expired_sessions()
            results['expired_sessions_cleaned'] = expired_sessions
        
        if cleanup_type in ['all', 'logs']:
            # Clean up old logs (older than 30 days)
            days = request.json.get('log_retention_days', 30) if request.json else 30
            from app.database import get_db_manager
            db_manager = get_db_manager()
            old_logs = db_manager.cleanup_old_logs(days)
            results['old_logs_cleaned'] = old_logs
        
        if cleanup_type in ['all', 'uploads']:
            # Clean up failed uploads
            file_handler = get_file_upload_handler()
            file_handler.cleanup_failed_uploads()
            results['failed_uploads_cleaned'] = True
        
        return jsonify({
            'message': 'Cleanup completed successfully',
            'results': results
        }), 200
        
    except Exception as e:
        logger.error(f"API cleanup error: {str(e)}")
        return jsonify({'error': 'Cleanup failed'}), 500

# Error handlers for API
@bp.errorhandler(404)
def api_not_found(error):
    return jsonify({'error': 'Endpoint not found'}), 404

@bp.errorhandler(405)
def api_method_not_allowed(error):
    return jsonify({'error': 'Method not allowed'}), 405

@bp.errorhandler(500)
def api_internal_error(error):
    return jsonify({'error': 'Internal server error'}), 500

@bp.route('/updates', methods=['GET', 'POST'])
def api_get_updates():
    """Get fixtures with updates since a specific timestamp - supports both JWT and API tokens"""
    try:
        from app.models import Match, User
        from flask_jwt_extended import jwt_required, get_jwt_identity, verify_jwt_in_request
        from app.auth.jwt_utils import validate_api_token, extract_token_from_request
        
        user = None
        auth_method = None
        
        # Try JWT authentication first (short-lived session tokens)
        try:
            verify_jwt_in_request()
            user_id = get_jwt_identity()
            user = User.query.get(user_id)
            auth_method = "JWT"
            logger.info(f"API access via JWT token by user {user.username if user else 'unknown'}")
        except Exception as jwt_error:
            logger.debug(f"JWT authentication failed: {str(jwt_error)}")
            
            # If JWT fails, try API token authentication (long-lived tokens)
            try:
                token = extract_token_from_request()
                if not token:
                    return jsonify({
                        'error': 'Authentication required',
                        'message': 'Either JWT token or API token required'
                    }), 401
                
                user, api_token = validate_api_token(token)
                auth_method = f"API Token ({api_token.name if api_token else 'JWT'})"
                logger.info(f"API access via API token by user {user.username}")
            except Exception as api_error:
                logger.debug(f"API token authentication failed: {str(api_error)}")
                return jsonify({
                    'error': 'Authentication failed',
                    'message': 'Invalid JWT token or API token'
                }), 401
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        logger.info(f"API updates accessed via {auth_method} by user {user.username} (ID: {user.id})")
        
        # Get 'from' parameter (unix timestamp) - now optional
        # Support both GET (query params) and POST (JSON body)
        if request.method == 'GET':
            from_timestamp = request.args.get('from')
        else:  # POST
            data = request.get_json() or {}
            from_timestamp = data.get('from')
        
        # Get the default fixtures count for both cases
        from app.models import SystemSettings
        default_fixtures_count = SystemSettings.get_setting('api_updates_default_count', 10)
        
        if from_timestamp is not None:
            # If 'from' parameter is provided, validate it and get fixtures after that timestamp
            try:
                from_timestamp = int(from_timestamp)
            except (ValueError, TypeError):
                return jsonify({'error': 'Parameter "from" must be a valid unix timestamp'}), 400
            
            # Get fixtures with active time after the given timestamp (limited to max count)
            fixtures_data = Match.get_fixtures_with_active_time(from_timestamp, limit=default_fixtures_count)
        else:
            # If 'from' parameter is not provided, get the last N activated fixtures
            fixtures_data = Match.get_last_activated_fixtures(default_fixtures_count)
            
            # Fallback: If no fixtures have fixture_active_time set, get active fixtures by active_status
            if not fixtures_data:
                logger.warning("No fixtures with fixture_active_time found, falling back to active_status")
                from sqlalchemy import func
                
                fallback_query = db.session.query(
                    Match.fixture_id,
                    Match.fixture_active_time,
                    Match.filename,
                    func.min(Match.created_at).label('created_at')
                ).filter(
                    Match.active_status == True
                ).group_by(
                    Match.fixture_id,
                    Match.fixture_active_time,
                    Match.filename
                )
                
                if from_timestamp is not None:
                    # For 'from' queries, order by created_at ascending and limit
                    fallback_query = fallback_query.order_by(func.min(Match.created_at).asc())
                else:
                    # For default queries, get most recent fixtures
                    fallback_query = fallback_query.order_by(func.min(Match.created_at).desc())
                
                fixtures_data = fallback_query.limit(default_fixtures_count).all()
        
        if not fixtures_data:
            return jsonify({
                'fixtures': [],
                'count': 0,
                'from_timestamp': from_timestamp
            }), 200
        
        result_fixtures = []
        
        for fixture_row in fixtures_data:
            fixture_id = fixture_row.fixture_id
            fixture_active_time = fixture_row.fixture_active_time
            filename = fixture_row.filename
            created_at = fixture_row.created_at
            
            # Get all matches for this fixture
            matches = Match.query.filter_by(fixture_id=fixture_id).all()
            
            # Prepare match data with zip download links
            matches_data = []
            for match in matches:
                match_dict = match.to_dict(include_outcomes=True)
                
                # Add zip file download link if available
                if match.zip_filename and match.zip_upload_status == 'completed':
                    from flask import url_for, current_app
                    import os
                    
                    # Construct download link using authenticated API endpoint
                    # Use the persistent ZIP uploads directory
                    zip_path = os.path.join(current_app.config.get('ZIP_UPLOADS_DIR', current_app.config.get('UPLOAD_FOLDER', 'uploads')), match.zip_filename)
                    if os.path.exists(zip_path):
                        match_dict['zip_download_url'] = url_for('api.api_download_zip',
                                                               match_id=match.id,
                                                               _external=True)
                    else:
                        match_dict['zip_download_url'] = None
                else:
                    match_dict['zip_download_url'] = None
                
                matches_data.append(match_dict)
            
            # Create fixture summary
            fixture_summary = {
                'fixture_id': fixture_id,
                'filename': filename,
                'fixture_active_time': fixture_active_time,
                'created_at': created_at.isoformat() if created_at else None,
                'matches_count': len(matches_data),
                'matches': matches_data
            }
            
            result_fixtures.append(fixture_summary)
        
        return jsonify({
            'fixtures': result_fixtures,
            'count': len(result_fixtures),
            'from_timestamp': from_timestamp
        }), 200
        
    except Exception as e:
        logger.error(f"API updates error: {str(e)}")
        return jsonify({'error': 'Failed to retrieve updates'}), 500

@bp.route('/download/zip/<int:match_id>', methods=['GET'])
def api_download_zip(match_id):
    """Download ZIP file for specific match - supports both JWT and API tokens"""
    try:
        from app.models import Match, User
        from flask_jwt_extended import jwt_required, get_jwt_identity, verify_jwt_in_request
        from app.auth.jwt_utils import validate_api_token, extract_token_from_request
        from flask import send_file
        import os
        
        user = None
        auth_method = None
        
        # Try JWT authentication first (short-lived session tokens)
        try:
            verify_jwt_in_request()
            user_id = get_jwt_identity()
            user = User.query.get(user_id)
            auth_method = "JWT"
            logger.info(f"ZIP download via JWT token by user {user.username if user else 'unknown'}")
        except Exception as jwt_error:
            logger.debug(f"JWT authentication failed: {str(jwt_error)}")
            
            # If JWT fails, try API token authentication (long-lived tokens)
            try:
                token = extract_token_from_request()
                if not token:
                    return jsonify({
                        'error': 'Authentication required',
                        'message': 'Either JWT token or API token required'
                    }), 401
                
                user, api_token = validate_api_token(token)
                auth_method = f"API Token ({api_token.name if api_token else 'JWT'})"
                logger.info(f"ZIP download via API token by user {user.username}")
            except Exception as api_error:
                logger.debug(f"API token authentication failed: {str(api_error)}")
                return jsonify({
                    'error': 'Authentication failed',
                    'message': 'Invalid JWT token or API token'
                }), 401
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        # Get match and verify access
        match = Match.query.get(match_id)
        if not match:
            return jsonify({'error': 'Match not found'}), 404
        
        # Check if user has access to this match (admin or creator)
        if not user.is_admin and match.created_by != user.id:
            return jsonify({'error': 'Access denied - you can only download ZIP files for your own matches'}), 403
        
        # Check if ZIP file exists
        if not match.zip_filename or match.zip_upload_status != 'completed':
            return jsonify({'error': 'ZIP file not available for this match'}), 404
        
        # Construct file path using persistent ZIP directory
        zip_path = os.path.join(current_app.config.get('ZIP_UPLOADS_DIR', current_app.config.get('UPLOAD_FOLDER', 'uploads')), match.zip_filename)
        
        if not os.path.exists(zip_path):
            return jsonify({'error': 'ZIP file not found on disk'}), 404
        
        logger.info(f"ZIP file download successful via {auth_method} by user {user.username} for match {match_id}")
        
        return send_file(zip_path, as_attachment=True, download_name=match.zip_filename)
        
    except Exception as e:
        logger.error(f"API ZIP download error: {str(e)}")
        return jsonify({'error': 'ZIP download failed'}), 500