"""
VYNDR Input Validation
Sanitize and validate all user inputs before processing.
Prevents injection, overflow, and malformed data.
"""

import re
import logging

logger = logging.getLogger('vyndr')

MAX_PLAYER_NAME = 100
MAX_STAT_TYPE = 50
MAX_SPORT = 10

VALID_STAT_TYPES = {
    'nba': ['points', 'rebounds', 'assists', 'threes', 'pts_reb_ast',
            'steals', 'blocks', 'turnovers'],
    'mlb': ['strikeouts', 'hits', 'home_runs', 'rbi', 'total_bases',
            'walks', 'runs', 'earned_runs', 'innings_pitched',
            'hits_allowed', 'stolen_bases',
            # Session 56 audit — keep in sync with analyze.js + scan.js.
            'doubles', 'outs']
}

VALID_SPORTS = ['nba', 'mlb']
VALID_OVER_UNDER = ['over', 'under']

SQL_INJECTION_PATTERNS = [
    'drop table', 'delete from', 'insert into',
    'union select', '--', ';--', 'or 1=1', "' or '",
    'exec(', 'execute(', 'xp_cmdshell'
]


def sanitize_string(value, max_length=100):
    """
    Remove dangerous characters and enforce length limit.

    Args:
        value: Input string.
        max_length: Maximum allowed length.

    Returns:
        Sanitized string, or None if input is invalid.
    """
    if not isinstance(value, str):
        return None
    value = value.strip()
    # Remove SQL injection characters
    value = re.sub(r'[;\'"\\`]', '', value)
    # Remove HTML/script tags
    value = re.sub(r'<[^>]+>', '', value)
    return value[:max_length] if value else None


def check_sql_injection(value):
    """
    Check if a string contains SQL injection patterns.

    Args:
        value: Input string to check.

    Returns:
        True if injection pattern detected, False otherwise.
    """
    if not value:
        return False
    lower = str(value).lower()
    return any(pattern in lower for pattern in SQL_INJECTION_PATTERNS)


def validate_grade_request(data, sport):
    """
    Validate a grade request body.

    Args:
        data: Request JSON body dict.
        sport: Sport string ('nba' or 'mlb').

    Returns:
        Tuple of (validated_data, error_message). One will be None.
    """
    if not data or not isinstance(data, dict):
        return None, 'Request body must be JSON object'

    if sport not in VALID_SPORTS:
        return None, f'Invalid sport: {sport}. Must be one of {VALID_SPORTS}'

    player_name = sanitize_string(data.get('player_name', ''), MAX_PLAYER_NAME)
    if not player_name:
        return None, 'player_name is required'

    stat_type = sanitize_string(data.get('stat_type', ''), MAX_STAT_TYPE)
    if stat_type not in VALID_STAT_TYPES.get(sport, []):
        return None, f'Invalid stat_type for {sport}. Must be one of {VALID_STAT_TYPES[sport]}'

    try:
        line = float(data.get('line', 0))
        if line < 0 or line > 500:
            return None, 'line must be between 0 and 500'
    except (TypeError, ValueError):
        return None, 'line must be a number'

    over_under = sanitize_string(data.get('over_under', ''), 10)
    if over_under not in VALID_OVER_UNDER:
        return None, f'over_under must be one of {VALID_OVER_UNDER}'

    return {
        'player_name': player_name,
        'stat_type': stat_type,
        'line': line,
        'over_under': over_under,
    }, None


def validate_image_upload(file_storage):
    """
    Validate image upload for OCR endpoint.
    Checks file size (max 10MB) and file type via magic bytes.

    Args:
        file_storage: Flask FileStorage object.

    Returns:
        Tuple of (validated_info, error_message).
    """
    if not file_storage:
        return None, 'No file provided'

    # Check file size
    file_storage.seek(0, 2)
    size = file_storage.tell()
    file_storage.seek(0)
    if size > 10 * 1024 * 1024:
        return None, 'File too large (max 10MB)'
    if size == 0:
        return None, 'Empty file'

    # Check magic bytes
    header = file_storage.read(8)
    file_storage.seek(0)

    valid_signatures = {
        b'\x89PNG': 'image/png',
        b'\xff\xd8\xff': 'image/jpeg',
        b'GIF87a': 'image/gif',
        b'GIF89a': 'image/gif',
    }

    file_type = None
    for sig, mime in valid_signatures.items():
        if header.startswith(sig):
            file_type = mime
            break

    if not file_type:
        return None, 'Invalid file type. Only PNG, JPEG, GIF accepted.'

    return {'file': file_storage, 'mime_type': file_type, 'size': size}, None


def validate_parlay_request(data):
    """
    Validate parlay grade request.

    Args:
        data: Request JSON body dict.

    Returns:
        Tuple of (validated_data, error_message).
    """
    if not data or not isinstance(data, dict):
        return None, 'Request body must be JSON object'

    legs = data.get('legs', [])
    if not isinstance(legs, list) or len(legs) < 2:
        return None, 'Parlay must have at least 2 legs'
    if len(legs) > 12:
        return None, 'Maximum 12 legs per parlay'

    for i, leg in enumerate(legs):
        if not isinstance(leg, dict):
            return None, f'Leg {i + 1} must be a JSON object'
        if 'player_name' not in leg or 'stat_type' not in leg:
            return None, f'Leg {i + 1} missing required fields'

    return data, None
