"""
Utilidades para el sistema de Perfumados
Funciones comunes, cálculos, validaciones
"""
import math
from datetime import datetime, timedelta
from functools import wraps
from flask import session, redirect, url_for, flash, request
from models import Usuario, LOCALES_CONFIG

# ── Zona horaria Chile ───────────────────────────────────────────────────────
# Chile usa UTC-4 en invierno (abril-oct) y UTC-3 en verano (oct-abril)
# Ajusta este offset si cambia el horario de verano
CHILE_TZ_OFFSET = timedelta(hours=-4)

def ahora_chile():
    """Retorna datetime actual en hora chilena (UTC-4)"""
    return datetime.utcnow() + CHILE_TZ_OFFSET

def hoy_chile():
    """Retorna la fecha de hoy en Chile"""
    return ahora_chile().date()


def calcular_distancia_haversine(lat1, lon1, lat2, lon2):
    """
    Calcula distancia en metros entre dos puntos GPS usando Haversine
    Args:
        lat1, lon1: Coordenadas punto 1
        lat2, lon2: Coordenadas punto 2
    Returns:
        Distancia en metros
    """
    R = 6371000  # Radio de la Tierra en metros

    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    delta_phi = math.radians(lat2 - lat1)
    delta_lambda = math.radians(lon2 - lon1)

    a = math.sin(delta_phi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2) ** 2
    c = 2 * math.asin(math.sqrt(a))

    return R * c


def validar_ubicacion_gps(local, lat, lng, radio_permitido=300):
    """
    Valida si la ubicación está dentro del radio permitido del local
    Args:
        local: Código del local ('perfumados', 'copihue', 'perfumarte')
        lat, lng: Coordenadas del usuario
        radio_permitido: Radio en metros (default 300m)
    Returns:
        dict: {'valido': bool, 'distancia': float, 'mensaje': str}
    """
    if local not in LOCALES_CONFIG:
        return {'valido': False, 'distancia': None, 'mensaje': 'Local no válido'}

    config = LOCALES_CONFIG[local]
    distancia = calcular_distancia_haversine(lat, lng, config['lat'], config['lng'])

    if distancia > radio_permitido:
        return {
            'valido': False,
            'distancia': round(distancia, 2),
            'mensaje': f'Estás {round(distancia, 0)}m lejos del local. Mínimo requerido: {radio_permitido}m'
        }

    return {
        'valido': True,
        'distancia': round(distancia, 2),
        'mensaje': f'Ubicación válida - {round(distancia, 1)}m del local'
    }


def login_required(f):
    """Decorador para requerir login"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('Debes iniciar sesión', 'warning')
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function


def admin_required(f):
    """Decorador para requerir rol admin o supervisor"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('Debes iniciar sesión', 'warning')
            return redirect(url_for('login'))

        usuario = Usuario.query.get(session['user_id'])
        if not usuario or usuario.rol not in ['admin', 'supervisor']:
            flash('No tienes permiso para acceder', 'danger')
            return redirect(url_for('dashboard'))

        return f(*args, **kwargs)
    return decorated_function


def super_admin_required(f):
    """Decorador para requerir rol admin"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('Debes iniciar sesión', 'warning')
            return redirect(url_for('login'))

        usuario = Usuario.query.get(session['user_id'])
        if not usuario or usuario.rol != 'admin':
            flash('Solo administradores pueden acceder', 'danger')
            return redirect(url_for('dashboard'))

        return f(*args, **kwargs)
    return decorated_function


def formatear_moneda(valor):
    """Formatea un valor como moneda chilena"""
    return f"${valor:,.0f}"


def formatear_fecha(fecha, formato='%d/%m/%Y'):
    """Formatea una fecha"""
    if isinstance(fecha, str):
        fecha = datetime.fromisoformat(fecha)
    return fecha.strftime(formato)


def get_mes_nombre(mes):
    """Retorna nombre del mes en español"""
    meses = {
        1: 'Enero', 2: 'Febrero', 3: 'Marzo', 4: 'Abril',
        5: 'Mayo', 6: 'Junio', 7: 'Julio', 8: 'Agosto',
        9: 'Septiembre', 10: 'Octubre', 11: 'Noviembre', 12: 'Diciembre'
    }
    return meses.get(mes, '')


def get_stats_dia(local, fecha):
    """
    Calcula estadísticas del día para un local
    Returns: dict con totales por forma de pago
    """
    from models import Venta

    ventas = Venta.query.filter_by(local=local, fecha=fecha).all()

    stats = {
        'total_ventas': len(ventas),
        'total_monto': 0,
        'efectivo': 0,
        'debito': 0,
        'credito': 0,
        'transferencia': 0,
        'otros': 0,
        # compatibilidad con campo genérico 'tarjeta'
        'tarjeta': 0,
        'por_turno': {'AM': 0, 'PM': 0},
        'ventas': ventas
    }

    for venta in ventas:
        stats['total_monto'] += venta.total
        fp = venta.forma_pago
        if fp in stats:
            stats[fp] += venta.total
        else:
            stats['otros'] += venta.total
        # acumular tarjeta genérica para compatibilidad
        if fp in ('debito', 'credito', 'tarjeta'):
            stats['tarjeta'] += venta.total
        turno = venta.turno if venta.turno in ('AM', 'PM') else 'AM'
        stats['por_turno'][turno] += venta.total

    return stats


def get_stats_rango(local, fecha_inicio, fecha_fin):
    """
    Calcula estadísticas para un rango de fechas
    """
    from models import Venta

    ventas = Venta.query.filter(
        Venta.local == local,
        Venta.fecha >= fecha_inicio,
        Venta.fecha <= fecha_fin
    ).all()

    total_monto = sum(v.total for v in ventas)
    stats = {
        'dias': (fecha_fin - fecha_inicio).days + 1,
        'total_ventas': len(ventas),
        'total_monto': total_monto,
        'promedio_dia': 0,
        'efectivo': sum(v.total for v in ventas if v.forma_pago == 'efectivo'),
        'debito': sum(v.total for v in ventas if v.forma_pago == 'debito'),
        'credito': sum(v.total for v in ventas if v.forma_pago == 'credito'),
        'transferencia': sum(v.total for v in ventas if v.forma_pago == 'transferencia'),
        'otros': sum(v.total for v in ventas if v.forma_pago not in ('efectivo', 'debito', 'credito', 'transferencia')),
        # tarjeta = débito + crédito para compatibilidad
        'tarjeta': sum(v.total for v in ventas if v.forma_pago in ('debito', 'credito', 'tarjeta')),
        'ventas': ventas,
    }

    if stats['dias'] > 0:
        stats['promedio_dia'] = stats['total_monto'] / stats['dias']

    return stats


def get_total_rango_locales(locales_list, fecha_inicio, fecha_fin):
    """
    Suma el total de ventas de varios locales en un rango de fechas.
    Útil para acumulados semanales y mensuales del dashboard.
    """
    from models import Venta
    if not locales_list:
        return 0
    ventas = Venta.query.filter(
        Venta.local.in_(locales_list),
        Venta.fecha >= fecha_inicio,
        Venta.fecha <= fecha_fin
    ).all()
    return sum(v.total for v in ventas)


def get_stats_consolidado_rango(locales_list, fecha_inicio, fecha_fin):
    """
    Stats consolidados (total + desglose por medio de pago) para
    una lista de locales en un rango de fechas.
    """
    from models import Venta
    if not locales_list:
        return {'total_monto': 0, 'total_ventas': 0,
                'efectivo': 0, 'debito': 0, 'credito': 0,
                'transferencia': 0, 'otros': 0}
    ventas = Venta.query.filter(
        Venta.local.in_(locales_list),
        Venta.fecha >= fecha_inicio,
        Venta.fecha <= fecha_fin
    ).all()
    result = {
        'total_monto': sum(v.total for v in ventas),
        'total_ventas': len(ventas),
        'efectivo':      sum(v.total for v in ventas if v.forma_pago == 'efectivo'),
        'debito':        sum(v.total for v in ventas if v.forma_pago == 'debito'),
        'credito':       sum(v.total for v in ventas if v.forma_pago == 'credito'),
        'transferencia': sum(v.total for v in ventas if v.forma_pago == 'transferencia'),
        'otros':         sum(v.total for v in ventas if v.forma_pago not in
                            ('efectivo', 'debito', 'credito', 'transferencia')),
    }
    return result


def validar_email(email):
    """Valida formato de email"""
    import re
    patron = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(patron, email) is not None


def validar_rut(rut):
    """Valida RUT chileno básico"""
    rut = rut.replace('.', '').replace('-', '').upper()
    if len(rut) < 8:
        return False
    return rut[:-1].isdigit() and rut[-1].isalnum()


def obtener_usuario_actual(user_id):
    """Obtiene el usuario actual desde la sesión"""
    return Usuario.query.get(user_id) if user_id else None
