use std::str::FromStr;

use cyberex_macro::EnumRenames;
use derive_more::From;
use serde::Deserialize;
use serde_with::{DisplayFromStr, serde_as};
type Ofloat32 = ordered_float::OrderedFloat<f32>;

#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub struct OrderedFloat32(Ofloat32);

impl Into<f32> for OrderedFloat32 {
    fn into(self) -> f32 {
        self.as_f32()
    }
}

impl From<f32> for OrderedFloat32 {
    fn from(value: f32) -> Self {
        Self(ordered_float::OrderedFloat::from(value))
    }
}
impl From<u32> for OrderedFloat32 {
    fn from(value: u32) -> Self {
        Self(ordered_float::OrderedFloat::from(value as f32))
    }
}

impl OrderedFloat32 {
    pub fn new(value: impl Into<Ofloat32>) -> Self {
        Self(value.into())
    }
    pub fn as_f32(&self) -> f32 {
        self.0.into_inner()
    }
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq, From)]
#[serde(untagged)]
pub enum Actions {
    Multi(Vec<Action>),
    Uni(Action),
}
impl Actions {
    pub fn as_muti(&self) -> Vec<Action> {
        match self {
            Actions::Multi(actions) => actions.to_owned(),
            Actions::Uni(action) => vec![action.clone()],
        }
    }
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, From, EnumRenames)]
#[serde(tag = "command")]
pub enum MediaCtrl {
    #[serde(rename = "playbackCtrl")]
    PlaybackCtrl(PlaybackCtrl),
}
impl MediaCtrl {
    pub fn command(&self) -> &str {
        self.rename()
    }
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct PlaybackCtrl {
    #[serde(rename = "params")]
    action: Actions,
}
impl PlaybackCtrl {
    pub fn new(action: impl Into<Action>) -> Self {
        Self {
            action: Actions::from(action.into()),
        }
    }
    pub fn new_muti_action<T, I>(actions: T) -> Self
    where
        T: IntoIterator<Item = I>,
        I: Into<Action>,
    {
        let actions = actions.into_iter().map(|v| v.into()).collect::<Vec<Action>>();
        Self {
            action: Actions::from(actions),
        }
    }

    pub fn as_muti_action(&self) -> Vec<Action> {
        self.action.as_muti()
    }
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, From)]
#[serde(rename_all = "lowercase")]
#[serde(tag = "action")]
pub enum Action {
    Drag(Darg),
    Speed(Speed),
    Back(Back),
    Pause,
    Resume,
}

#[serde_as]
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Darg {
    #[serde_as(as = "DisplayFromStr")]
    range: DargRange,
}
impl Darg {
    pub fn new(range: DargRange) -> Self {
        Self { range }
    }
    pub fn range(&self) -> &DargRange {
        &self.range
    }
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct DargRange {
    from: i64,
    to: i64,
}
impl DargRange {
    pub fn new(from_sec: i64, to_sec: i64) -> Self {
        Self {
            from: from_sec,
            to: to_sec,
        }
    }
    pub fn range_from(&self) -> i64 {
        self.from as _
    }
    pub fn range_to(&self) -> i64 {
        self.to
    }
}
impl FromStr for DargRange {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Ok(from) = s.parse() {
            return Ok(Self { from, to: 0 });
        }
        match s.split_once("-") {
            Some((from_str, to_str)) => {
                let from = from_str.parse()?;
                let to = to_str.parse().unwrap_or(0);

                Ok(Self { from, to })
            },
            None => {
                let from = s.parse()?;
                Ok(Self { from, to: 0 })
            },
        }
    }
}
#[serde_as]
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Speed {
    #[serde_as(as = "DisplayFromStr")]
    #[serde(rename = "speed")]
    direction: SpeedDirection,
}

impl Speed {
    pub fn new(speed: impl Into<SpeedDirection>) -> Self {
        let direction = speed.into();
        Self { direction }
    }
    pub fn direction(&self) -> &SpeedDirection {
        &self.direction
    }
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum SpeedDirection {
    Up(OrderedFloat32),
    Down(OrderedFloat32),
}
impl SpeedDirection {
    pub fn new(speed: impl Into<OrderedFloat32>) -> Self {
        let number: f32 = speed.into().into();
        if number > 0.0 {
            Self::up(number)
        } else {
            Self::down(number.abs())
        }
    }
    pub fn up(speed: impl Into<OrderedFloat32>) -> Self {
        Self::Up(speed.into())
    }
    pub fn down(speed: impl Into<OrderedFloat32>) -> Self {
        Self::Down(speed.into())
    }
    pub fn as_f32(&self) -> f32 {
        self.as_order_f32().into_inner()
    }
    fn as_order_f32(&self) -> Ofloat32 {
        match self {
            SpeedDirection::Up(v) => v.0,
            SpeedDirection::Down(v) => Ofloat32::from(-v.as_f32()),
        }
    }
}

impl FromStr for SpeedDirection {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let number: f32 = s.parse()?;

        Ok(Self::new(number))
    }
}

#[serde_as]
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Back {
    #[serde_as(as = "DisplayFromStr")]
    speed: usize,
}
impl Back {
    pub fn new(speed: usize) -> Self {
        Self { speed }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_case_mediactrl_drag_deserilize() {
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":{"action":"drag","range":"200"}}"#,
            )
            .unwrap(),
            PlaybackCtrl::new(Darg::new(DargRange::new(200, 0))).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":{"action":"drag","range":"200-400"}}"#,
            )
            .unwrap(),
            PlaybackCtrl::new(Darg::new(DargRange::new(200, 400))).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":{"action":"drag","range":"-400"}}"#,
            )
            .unwrap(),
            PlaybackCtrl::new(Darg::new(DargRange::new(-400, 0))).into()
        );
    }

    #[test]
    fn test_case_mediactrl_speed_deserilize() {
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":[{"action":"speed","speed":"2"}]}"#
            )
            .unwrap(),
            PlaybackCtrl::new_muti_action([Speed::new(SpeedDirection::up(2))]).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":[{"action":"speed","speed":"2.1"}]}"#
            )
            .unwrap(),
            PlaybackCtrl::new_muti_action([Speed::new(SpeedDirection::new(2.1))]).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(r#"{"command":"playbackCtrl","params":{"action":"speed","speed":"2"}}"#)
                .unwrap(),
            PlaybackCtrl::new(Speed::new(SpeedDirection::up(2))).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":{"action":"speed","speed":"-2"}}"#,
            )
            .unwrap(),
            PlaybackCtrl::new(Speed::new(SpeedDirection::down(2))).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":{"action":"speed","speed":"-2.5"}}"#,
            )
            .unwrap(),
            PlaybackCtrl::new(Speed::new(SpeedDirection::new(-2.5))).into()
        );
    }

    #[test]
    fn test_case_mediactrl_other_deserilize() {
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(r#"{"command":"playbackCtrl","params":{"action":"pause"}}"#).unwrap(),
            PlaybackCtrl::new(Action::Pause).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(r#"{"command":"playbackCtrl","params":{"action":"resume"}}"#,).unwrap(),
            PlaybackCtrl::new(Action::Resume).into()
        );
        assert_eq!(
            serde_json::from_str::<MediaCtrl>(
                r#"{"command":"playbackCtrl","params":{"action":"back", "speed": "1"}}"#,
            )
            .unwrap(),
            PlaybackCtrl::new(Action::Back(Back::new(1))).into()
        );
    }

    #[test]
    fn test_case_speed_direction() {
        assert_eq!(SpeedDirection::new(1.0).as_order_f32(), Ofloat32::from(1.0));
        assert_eq!(SpeedDirection::up(1.0).as_order_f32(), Ofloat32::from(1.0));

        assert_eq!(SpeedDirection::new(-1.2).as_order_f32(), Ofloat32::from(-1.2));
        assert_eq!(SpeedDirection::down(1.2).as_order_f32(), Ofloat32::from(-1.2));
    }

    #[test]
    fn test_case_order_float() {
        let f = OrderedFloat32::new(4.0);
        let result = f.as_f32() / 3.0;
        assert_eq!(result, 4.0 / 3.0);
    }
}
