use serde::{Deserialize, Serialize};

/// Sleep timer mode - determines when playback should stop
/// TRACES: UR-026 | DR-029
#[derive(specta::Type, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum SleepTimerMode {
    /// Timer is off
    Off,
    /// Stop after a specific time duration
    Time {
        #[serde(rename = "endTime")]
        end_time: i64, // Unix timestamp in milliseconds
    },
    /// Stop at the end of current track
    EndOfTrack,
    /// Stop after N more episodes complete (TV episodes only, not audio tracks)
    Episodes { remaining: u32 },
}

/// Sleep timer state
#[derive(specta::Type, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SleepTimerState {
    pub mode: SleepTimerMode,
    pub remaining_seconds: u32,
}

impl Default for SleepTimerState {
    fn default() -> Self {
        Self {
            mode: SleepTimerMode::Off,
            remaining_seconds: 0,
        }
    }
}

impl SleepTimerState {
    /// Check if timer is active
    pub fn is_active(&self) -> bool {
        !matches!(self.mode, SleepTimerMode::Off)
    }

    /// Update remaining seconds for time-based timer
    pub fn update_remaining_seconds(&mut self) {
        if let SleepTimerMode::Time { end_time } = self.mode {
            let now = chrono::Utc::now().timestamp_millis();
            self.remaining_seconds = if now >= end_time {
                0
            } else {
                ((end_time - now) / 1000).max(0) as u32
            };
        }
    }

    /// Decrement episode counter, returns true if should stop
    /// Only counts TV episodes, not audio tracks
    pub fn decrement_episode(&mut self) -> bool {
        match &mut self.mode {
            SleepTimerMode::Episodes { remaining } => {
                if *remaining <= 1 {
                    self.mode = SleepTimerMode::Off;
                    self.remaining_seconds = 0;
                    true
                } else {
                    *remaining -= 1;
                    false
                }
            }
            _ => false,
        }
    }

    /// Cancel the timer
    pub fn cancel(&mut self) {
        self.mode = SleepTimerMode::Off;
        self.remaining_seconds = 0;
    }
}

// TRACES: UR-026 | DR-029 | UT-012
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sleep_timer_episode_decrement() {
        let mut timer = SleepTimerState {
            mode: SleepTimerMode::Episodes { remaining: 2 },
            remaining_seconds: 0,
        };

        assert!(!timer.decrement_episode());
        assert!(matches!(
            timer.mode,
            SleepTimerMode::Episodes { remaining: 1 }
        ));

        assert!(timer.decrement_episode());
        assert!(matches!(timer.mode, SleepTimerMode::Off));
    }

    #[test]
    fn test_sleep_timer_is_active() {
        let timer = SleepTimerState::default();
        assert!(!timer.is_active());

        let timer = SleepTimerState {
            mode: SleepTimerMode::Time { end_time: 0 },
            remaining_seconds: 0,
        };
        assert!(timer.is_active());

        let timer = SleepTimerState {
            mode: SleepTimerMode::EndOfTrack,
            remaining_seconds: 0,
        };
        assert!(timer.is_active());

        let timer = SleepTimerState {
            mode: SleepTimerMode::Episodes { remaining: 3 },
            remaining_seconds: 0,
        };
        assert!(timer.is_active());
    }

    #[test]
    fn test_sleep_timer_cancel() {
        let mut timer = SleepTimerState {
            mode: SleepTimerMode::Episodes { remaining: 5 },
            remaining_seconds: 300,
        };

        timer.cancel();
        assert!(matches!(timer.mode, SleepTimerMode::Off));
        assert_eq!(timer.remaining_seconds, 0);
    }

    #[test]
    fn test_update_remaining_seconds() {
        let end_time = chrono::Utc::now().timestamp_millis() + 30000; // 30 seconds from now
        let mut timer = SleepTimerState {
            mode: SleepTimerMode::Time { end_time },
            remaining_seconds: 0,
        };

        timer.update_remaining_seconds();
        // Should be approximately 30 seconds (allow for small time difference)
        assert!(timer.remaining_seconds >= 29 && timer.remaining_seconds <= 31);
    }
}
