import math
from typing import Union, Optional
from ..base_movimiento import Movimiento
from ...units import ureg, Q_

class MovimientoRectilineoUniformementeVariado(Movimiento):
    """
    Clase para calcular posición, velocidad y aceleración en Movimiento Rectilíneo Uniformemente Variado (MRUV).
    """

    def __init__(self, posicion_inicial: Union[float, Q_] = 0.0 * ureg.meter, velocidad_inicial: Union[float, Q_] = 0.0 * ureg.meter / ureg.second, aceleracion_inicial: Union[float, Q_] = 0.0 * ureg.meter / ureg.second**2) -> None:
        """
        Inicializa el objeto MovimientoRectilineoUniformementeVariado con condiciones iniciales.

        Args:
            posicion_inicial (Q_): Posición inicial del objeto (m).
            velocidad_inicial (Q_): Velocidad inicial del objeto (m/s).
            aceleracion_inicial (Q_): Aceleración inicial del objeto (m/s^2).
        """
        if not isinstance(posicion_inicial, Q_):
            posicion_inicial = Q_(posicion_inicial, ureg.meter)
        if not isinstance(velocidad_inicial, Q_):
            velocidad_inicial = Q_(velocidad_inicial, ureg.meter / ureg.second)
        if not isinstance(aceleracion_inicial, Q_):
            aceleracion_inicial = Q_(aceleracion_inicial, ureg.meter / ureg.second**2)

        self.posicion_inicial = posicion_inicial
        self.velocidad_inicial = velocidad_inicial
        self.aceleracion_inicial = aceleracion_inicial

    def posicion(self, tiempo: Union[float, Q_]) -> Q_:
        """
        Calcula la posición en MRUV.
        Ecuación: x = x0 + v0 * t + 0.5 * a * t^2

        Args:
            tiempo (Q_): Tiempo transcurrido (s).

        Returns:
            Q_: Posición final (m).
        
        Raises:
            ValueError: Si el tiempo es negativo.
        """
        if not isinstance(tiempo, Q_):
            tiempo = Q_(tiempo, ureg.second)
        if tiempo.magnitude < 0:
            raise ValueError("El tiempo no puede ser negativo.")
        return self.posicion_inicial + self.velocidad_inicial * tiempo + 0.5 * self.aceleracion_inicial * (tiempo ** 2)

    def velocidad(self, tiempo: Union[float, Q_]) -> Q_:
        """
        Calcula la velocidad en MRUV.
        Ecuación: v = v0 + a * t

        Args:
            tiempo (Q_): Tiempo transcurrido (s).

        Returns:
            Q_: Velocidad final (m/s).
        
        Raises:
            ValueError: Si el tiempo es negativo.
        """
        if not isinstance(tiempo, Q_):
            tiempo = Q_(tiempo, ureg.second)
        if tiempo.magnitude < 0:
            raise ValueError("El tiempo no puede ser negativo.")
        return self.velocidad_inicial + self.aceleracion_inicial * tiempo

    def velocidad_sin_tiempo(self, posicion_final: Union[float, Q_]) -> Q_:
        """
        Calcula la velocidad usando la ecuación v^2 = v0^2 + 2*a*Δx.
        Esta ecuación es útil cuando no se conoce el tiempo.

        Args:
            posicion_final (Q_): Posición final (m).

        Returns:
            Q_: Velocidad (m/s).
        """
        if not isinstance(posicion_final, Q_):
            posicion_final = Q_(posicion_final, ureg.meter)
        
        delta_x = posicion_final - self.posicion_inicial
        v_squared = self.velocidad_inicial**2 + 2 * self.aceleracion_inicial * delta_x
        
        if v_squared.magnitude < 0:
            raise ValueError("No se puede calcular la velocidad real para esta posición (velocidad al cuadrado negativa).")
        
        # Determine the sign of the velocity
        # This is a simplification, a more robust solution might involve checking the direction of motion
        # or considering the context of the problem.
        # For now, we'll assume the sign is determined by the initial velocity and acceleration over a small time step.
        test_time = 1 * ureg.second # Use a small positive time to check direction
        test_velocity = self.velocidad_inicial + self.aceleracion_inicial * test_time
        
        return Q_(math.sqrt(v_squared.magnitude), ureg.meter / ureg.second) * (1 if test_velocity.magnitude >= 0 else -1)


    def aceleracion(self, tiempo: Optional[Union[float, Q_]] = None) -> Q_:
        """
        Calcula la aceleración en MRUV (es constante).
        Ecuación: a = a0

        Args:
            tiempo (Q_, optional): Tiempo transcurrido (s). No afecta al resultado.

        Returns:
            Q_: Aceleración (m/s^2).
        """
        return self.aceleracion_inicial
