from flask import Blueprint, request, jsonify
from sqlalchemy import func, or_, String
from app.database import db
from app.models import Tire, TireType, TipoLlanta, MarcaLlanta, ModeloLlanta
from app.auth import require_super_admin
import re

tires_bp = Blueprint('tires', __name__)

def validate_text(text):
    """Validate text - prevent dangerous characters like <, >, &, etc."""
    if not text:
        return True  # Allow empty text
    text_regex = r'^[a-zA-Z0-9áéíóúÁÉÍÓÚñÑüÜ\s.,;:!?\-()/]+$'
    return bool(re.match(text_regex, text))

def validate_url(url):
    """Validate URL format"""
    if not url:
        return True  # Allow empty URL (optional field)
    url_regex = r'^https?://.+'
    return bool(re.match(url_regex, url))

def validate_number(value, allow_decimal=False):
    """Validate number - only allow positive numbers"""
    if value is None:
        return True
    try:
        num = float(value) if allow_decimal else int(value)
        return num > 0
    except (ValueError, TypeError):
        return False

def tire_to_dict(tire):
    """Convert Tire model to dictionary."""
    try:
        # Obtener valores de las relaciones normalizadas o columnas antiguas (compatibilidad)
        brand_name = ""
        model_name = ""
        type_name = ""
        
        # Intentar obtener de relaciones normalizadas primero
        if hasattr(tire, 'marca_llanta_rel') and tire.marca_llanta_rel:
            brand_name = tire.marca_llanta_rel.nombre
        elif hasattr(tire, 'marca_id') and tire.marca_id:
            marca = MarcaLlanta.query.get(tire.marca_id)
            if marca:
                brand_name = marca.nombre
        else:
            # Fallback a columna antigua
            brand_name = getattr(tire, 'marca', '') or ""
        
        if hasattr(tire, 'modelo_llanta_rel') and tire.modelo_llanta_rel:
            model_name = tire.modelo_llanta_rel.nombre
        elif hasattr(tire, 'modelo_id') and tire.modelo_id:
            modelo = ModeloLlanta.query.get(tire.modelo_id)
            if modelo:
                model_name = modelo.nombre
        else:
            # Fallback a columna antigua
            model_name = getattr(tire, 'modelo', '') or ""
        
        if hasattr(tire, 'tipo_llanta_rel') and tire.tipo_llanta_rel:
            type_name = tire.tipo_llanta_rel.nombre
        elif hasattr(tire, 'tipo_id') and tire.tipo_id:
            tipo = TipoLlanta.query.get(tire.tipo_id)
            if tipo:
                type_name = tipo.nombre
        else:
            # Fallback a columna antigua o Enum
            tipo_raw = getattr(tire, 'tipo', None)
            if isinstance(tipo_raw, TireType):
                type_name = tipo_raw.value
            elif isinstance(tipo_raw, str):
                type_name = tipo_raw
            elif tipo_raw:
                type_name = str(tipo_raw)
            else:
                type_name = ""
        
        return {
            'id': tire.id,
            'brand': brand_name,
            'model': model_name,
            'size': {
                'width': tire.ancho,
                'aspectRatio': tire.relacion_aspecto,
                'diameter': tire.diametro
            },
            'type': type_name,
            'imageUrl': tire.url_imagen,
            'created_at': tire.creado_en.isoformat() if tire.creado_en else None
        }
    except Exception as e:
        import traceback
        error_msg = traceback.format_exc()
        print(f"Error in tire_to_dict for tire {tire.id}: {error_msg}")
        # Return with empty values if there's an error
        return {
            'id': tire.id,
            'brand': getattr(tire, 'marca', '') or "",
            'model': getattr(tire, 'modelo', '') or "",
            'size': {
                'width': tire.ancho,
                'aspectRatio': tire.relacion_aspecto,
                'diameter': tire.diametro
            },
            'type': "",
            'imageUrl': tire.url_imagen,
            'created_at': tire.creado_en.isoformat() if tire.creado_en else None
        }

@tires_bp.route('', methods=['GET'])
def get_tires():
    """Get all tires with optional filters. Public endpoint."""
    try:
        brand = request.args.get('brand')
        tire_type = request.args.get('type')
        width = request.args.get('width', type=int)
        aspect_ratio = request.args.get('ratio', type=int)
        diameter = request.args.get('diameter', type=int)
        search = request.args.get('search')  # Búsqueda por texto completo
        skip = request.args.get('skip', 0, type=int)
        limit = request.args.get('limit', 100, type=int)
        
        query = Tire.query
        
        # Búsqueda por texto completo (marca, modelo o combinación)
        if search:
            search_term = f'%{search.lower()}%'
            query = query.outerjoin(MarcaLlanta, Tire.marca_id == MarcaLlanta.id).outerjoin(
                ModeloLlanta, Tire.modelo_id == ModeloLlanta.id
            ).filter(
                or_(
                    func.lower(MarcaLlanta.nombre).like(search_term),
                    func.lower(Tire.marca).like(search_term),
                    func.lower(ModeloLlanta.nombre).like(search_term),
                    func.lower(Tire.modelo).like(search_term),
                    # Búsqueda combinada: "marca modelo"
                    func.concat(
                        func.coalesce(MarcaLlanta.nombre, Tire.marca, ''),
                        ' ',
                        func.coalesce(ModeloLlanta.nombre, Tire.modelo, '')
                    ).like(search_term)
                )
            ).distinct()
        elif brand:
            # Buscar por marca normalizada o columna antigua (compatibilidad)
            query = query.outerjoin(MarcaLlanta, Tire.marca_id == MarcaLlanta.id).filter(
                or_(
                    func.lower(MarcaLlanta.nombre).like(f'%{brand.lower()}%'),
                    func.lower(Tire.marca).like(f'%{brand.lower()}%')
                )
            ).distinct()
        if tire_type:
            # Filtrar por tipo normalizado o columna antigua (compatibilidad)
            query = query.outerjoin(TipoLlanta, Tire.tipo_id == TipoLlanta.id).filter(
                or_(
                    TipoLlanta.nombre == tire_type,
                    Tire.tipo == tire_type,
                    func.cast(Tire.tipo, String).like(f'%{tire_type}%')
                )
            ).distinct()
        if width:
            query = query.filter(Tire.ancho == width)
        if aspect_ratio:
            query = query.filter(Tire.relacion_aspecto == aspect_ratio)
        if diameter:
            query = query.filter(Tire.diametro == diameter)
        
        tires = query.offset(skip).limit(limit).all()
        return jsonify([tire_to_dict(t) for t in tires]), 200
    except Exception as e:
        import traceback
        error_msg = traceback.format_exc()
        print(f"Error in get_tires: {error_msg}")
        return jsonify({'error': str(e), 'details': error_msg}), 500

@tires_bp.route('/<tire_id>', methods=['GET'])
def get_tire(tire_id):
    """Get a specific tire by ID. Public endpoint."""
    tire = Tire.query.filter_by(id=tire_id).first()
    if not tire:
        return jsonify({'error': 'Tire not found'}), 404
    return jsonify(tire_to_dict(tire)), 200

@tires_bp.route('', methods=['POST'])
@require_super_admin
def create_tire():
    """Create a new tire. Super admin only."""
    data = request.get_json()
    
    tire_id = data.get('id')
    if tire_id:
        existing = Tire.query.filter_by(id=tire_id).first()
        if existing:
            return jsonify({'error': 'Tire ID already exists'}), 400
    
    brand = data.get('brand')
    model = data.get('model')
    size = data.get('size', {})
    tire_type = data.get('type')
    image_url = data.get('imageUrl')
    
    if not all([brand, model, size, tire_type]):
        return jsonify({'error': 'Missing required fields'}), 400
    
    # Validate text fields
    if brand and not validate_text(brand):
        return jsonify({'error': 'La marca contiene caracteres no permitidos'}), 400
    if model and not validate_text(model):
        return jsonify({'error': 'El modelo contiene caracteres no permitidos'}), 400
    
    # Validate numbers
    width = size.get('width')
    aspect_ratio = size.get('aspectRatio')
    diameter = size.get('diameter')
    
    if width and not validate_number(width):
        return jsonify({'error': 'El ancho debe ser un número positivo'}), 400
    if aspect_ratio and not validate_number(aspect_ratio):
        return jsonify({'error': 'El perfil debe ser un número positivo'}), 400
    if diameter and not validate_number(diameter):
        return jsonify({'error': 'El diámetro debe ser un número positivo'}), 400
    
    # Validate URL
    if image_url and not validate_url(image_url):
        return jsonify({'error': 'La URL de imagen debe comenzar con http:// o https://'}), 400
    
    new_tire = Tire(
        id=tire_id or f"tire-{brand.lower()}-{model.lower().replace(' ', '-')}",
        brand=brand,
        model=model,
        width=size.get('width'),
        aspect_ratio=size.get('aspectRatio'),
        diameter=size.get('diameter'),
        type=TireType(tire_type),
        image_url=image_url
    )
    
    db.session.add(new_tire)
    db.session.commit()
    
    return jsonify(tire_to_dict(new_tire)), 201

@tires_bp.route('/<tire_id>', methods=['PUT'])
@require_super_admin
def update_tire(tire_id):
    """Update a tire. Super admin only."""
    tire = Tire.query.filter_by(id=tire_id).first()
    if not tire:
        return jsonify({'error': 'Tire not found'}), 404
    
    data = request.get_json()
    
    if 'brand' in data:
        brand = data['brand']
        if brand and not validate_text(brand):
            return jsonify({'error': 'La marca contiene caracteres no permitidos'}), 400
        tire.brand = brand
    if 'model' in data:
        model = data['model']
        if model and not validate_text(model):
            return jsonify({'error': 'El modelo contiene caracteres no permitidos'}), 400
        tire.model = model
    if 'size' in data:
        size = data['size']
        if 'width' in size:
            width = size['width']
            if width and not validate_number(width):
                return jsonify({'error': 'El ancho debe ser un número positivo'}), 400
            tire.width = width
        if 'aspectRatio' in size:
            aspect_ratio = size['aspectRatio']
            if aspect_ratio and not validate_number(aspect_ratio):
                return jsonify({'error': 'El perfil debe ser un número positivo'}), 400
            tire.aspect_ratio = aspect_ratio
        if 'diameter' in size:
            diameter = size['diameter']
            if diameter and not validate_number(diameter):
                return jsonify({'error': 'El diámetro debe ser un número positivo'}), 400
            tire.diameter = diameter
    if 'type' in data:
        tire.type = TireType(data['type'])
    if 'imageUrl' in data:
        image_url = data['imageUrl']
        if image_url and not validate_url(image_url):
            return jsonify({'error': 'La URL de imagen debe comenzar con http:// o https://'}), 400
        tire.image_url = image_url
    
    db.session.commit()
    
    return jsonify(tire_to_dict(tire)), 200

@tires_bp.route('/<tire_id>', methods=['DELETE'])
@require_super_admin
def delete_tire(tire_id):
    """Delete a tire. Super admin only."""
    tire = Tire.query.filter_by(id=tire_id).first()
    if not tire:
        return jsonify({'error': 'Tire not found'}), 404
    
    db.session.delete(tire)
    db.session.commit()
    
    return '', 204
