//! Counter with animated gradient background
//!
//! This example demonstrates:
//! - State management with Bevy states
//! - Timer-based animations
//! - Background color transitions
//! - Custom widget rendering
//! - Proper logging with #[instrument]
//!
//! Keys:
//! - Left & Right: modify the counter
//! - Q or Esc: quit
//! - P: panic (tests panic handling)

use core::panic;
use std::time::Duration;

use bevy::{app::AppExit, diagnostic::FrameCount, prelude::*};
use bevy::{app::ScheduleRunnerPlugin, state::app::StatesPlugin};
use bevy_ratatui::event::KeyEvent;
use bevy_ratatui::{RatatuiContext, RatatuiPlugins};
use ratatui::crossterm::event::KeyEventKind;
use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Color, Style},
    text::Line,
    widgets::WidgetRef,
};
use tracing::instrument;

fn main() {
    let mut app = App::new();

    app.add_plugins((
        // Disable LogPlugin for clean TUI output
        MinimalPlugins
            .set(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f32(1. / 60.)))
            .build()
            .disable::<bevy::log::LogPlugin>(),
        StatesPlugin,
        RatatuiPlugins::default(),
    ))
    .add_systems(Startup, log_startup)
    .add_systems(PreUpdate, keyboard_input_system);

    app.init_resource::<BackgroundColor>()
        .init_resource::<Counter>()
        .init_state::<AppState>()
        .add_event::<CounterEvent>()
        .add_systems(
            Update,
            (ui_system, update_counter_system, background_color_system),
        )
        .add_systems(OnEnter(AppState::Negative), start_background_color_timer)
        .add_systems(OnEnter(AppState::Positive), start_background_color_timer)
        .run();
}

#[instrument(skip_all)]
fn log_startup() {
    info!("Gradient example started");
    debug!("Initial state: Positive (blue background)");
}

#[instrument(skip_all)]
fn ui_system(
    mut context: ResMut<RatatuiContext>,
    frame_count: Res<FrameCount>,
    counter: Res<Counter>,
    app_state: Res<State<AppState>>,
    bg_color: Res<BackgroundColor>,
) -> Result {
    context.draw(|frame| {
        let area = frame.area();
        let frame_count = Line::from(format!("Frame Count: {}", frame_count.0)).right_aligned();
        frame.render_widget(bg_color.as_ref(), area);
        frame.render_widget(frame_count, area);
        frame.render_widget(counter.as_ref(), area);
        frame.render_widget(app_state.get(), area)
    })?;

    Ok(())
}

#[instrument(skip_all)]
fn keyboard_input_system(
    mut events: EventReader<KeyEvent>,
    mut app_exit: EventWriter<AppExit>,
    mut counter_events: EventWriter<CounterEvent>,
) {
    use ratatui::crossterm::event::KeyCode;
    for event in events.read() {
        if let KeyEventKind::Release = event.kind {
            continue;
        }

        match event.code {
            KeyCode::Char('q') | KeyCode::Esc => {
                info!("Exit key pressed");
                app_exit.write_default();
            }
            KeyCode::Char('p') => {
                error!("Panic key pressed - deliberately panicking!");
                panic!("Panic!");
            }
            KeyCode::Left => {
                debug!("Decrement key pressed");
                counter_events.write(CounterEvent::Decrement);
            }
            KeyCode::Right => {
                debug!("Increment key pressed");
                counter_events.write(CounterEvent::Increment);
            }
            _ => {}
        }
    }
}

#[derive(Default, Resource, Debug, Deref, DerefMut)]
struct Counter(i32);

impl Counter {
    fn decrement(&mut self) {
        self.0 -= 1;
    }

    fn increment(&mut self) {
        self.0 += 1;
    }
}

#[derive(Debug, Clone, Copy, Event, PartialEq, Eq)]
enum CounterEvent {
    Increment,
    Decrement,
}

#[instrument(skip_all)]
fn update_counter_system(
    mut counter: ResMut<Counter>,
    mut events: EventReader<CounterEvent>,
    mut app_state: ResMut<NextState<AppState>>,
) {
    for event in events.read() {
        match event {
            CounterEvent::Increment => {
                counter.increment();
                info!("Counter incremented to {}", counter.0);
            }
            CounterEvent::Decrement => {
                counter.decrement();
                info!("Counter decremented to {}", counter.0);
            }
        }

        // Update app state based on counter value
        let new_state = if counter.0 < 0 {
            AppState::Negative
        } else {
            AppState::Positive
        };

        debug!("State changing to {:?}", new_state);
        app_state.set(new_state);
    }
}

impl WidgetRef for Counter {
    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
        let counter = format!("Counter: {}", self.0);
        Line::from(counter).render_ref(area, buf);
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
enum AppState {
    Negative,
    #[default]
    Positive,
}

impl WidgetRef for AppState {
    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
        let state = match self {
            AppState::Negative => "Negative",
            AppState::Positive => "Positive",
        };
        Line::from(state).centered().render_ref(area, buf);
    }
}

#[derive(Debug, Component, Deref, DerefMut)]
struct ColorChangeTimer {
    #[deref]
    timer: Timer,
    start_color: Color,
}

#[instrument(skip_all)]
fn start_background_color_timer(mut commands: Commands, bg_color: Res<BackgroundColor>) {
    info!("Starting background color transition");
    commands.spawn(ColorChangeTimer {
        timer: Timer::from_seconds(2.0, TimerMode::Once),
        start_color: bg_color.0,
    });
}

#[derive(Debug, Resource, Deref, DerefMut)]
struct BackgroundColor(Color);

impl Default for BackgroundColor {
    fn default() -> Self {
        BackgroundColor(Color::Rgb(0, 0, 0))
    }
}

impl WidgetRef for BackgroundColor {
    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
        buf.set_style(area, Style::new().bg(self.0));
    }
}

/// Change the background color over time when the app state changes from negative to positive
/// or vice versa.
#[instrument(skip_all)]
fn background_color_system(
    time: Res<Time>,
    mut query: Query<(Entity, &mut ColorChangeTimer)>,
    app_state: Res<State<AppState>>,
    mut commands: Commands,
    mut bg_color: ResMut<BackgroundColor>,
) {
    // Use log let-else pattern - get the single timer or return gracefully
    let mut iter = query.iter_mut();
    let Some((entity, mut timer)) = iter.next() else {
        // No timer active - this is normal when not transitioning
        return;
    };

    // Ensure there's only one timer
    if iter.next().is_some() {
        warn!("Multiple ColorChangeTimers found - expected only one");
    }

    timer.tick(time.delta());
    let end_color = match app_state.get() {
        AppState::Negative => Color::Rgb(191, 0, 0),
        AppState::Positive => Color::Rgb(0, 63, 128),
    };

    let Some(interpolated) = interpolate(timer.start_color, end_color, timer.fraction()) else {
        error!("Failed to interpolate colors - invalid color format");
        commands.entity(entity).despawn();
        return;
    };

    bg_color.0 = interpolated;

    if timer.just_finished() {
        debug!("Background color transition complete");
        commands.entity(entity).despawn();
    }
}

/// Interpolate between two colors based on the fraction
///
/// This is just a simple linear interpolation between the two colors (a real implementation would
/// use a color space that is perceptually uniform).
fn interpolate(start: Color, end: Color, fraction: f32) -> Option<Color> {
    let Color::Rgb(start_red, start_green, start_blue) = start else {
        return None;
    };
    let Color::Rgb(end_red, end_green, end_blue) = end else {
        return None;
    };
    Some(Color::Rgb(
        (start_red as f32 + (end_red as f32 - start_red as f32) * fraction) as u8,
        (start_green as f32 + (end_green as f32 - start_green as f32) * fraction) as u8,
        (start_blue as f32 + (end_blue as f32 - start_blue as f32) * fraction) as u8,
    ))
}
