use std::ops::Deref;

use bevy::{color::palettes::tailwind::*, prelude::*};
use calc::*;
use itertools::Itertools;
use rust_decimal::prelude::*;

fn main() {
    App::new()
        .insert_resource(ClearColor(SLATE_950.into()))
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, startup)
        .add_systems(Update, button_system)
        .observe(calculate)
        .run();
}

fn calculate(
    trigger: Trigger<PressEvent>,
    mut display: Local<String>,
    mut text_query: Query<&mut Text, With<Output>>,
) {
    match trigger.event().0.deref() {
        "C" => {
            *display = "".to_string();
        }
        "=" => {
            let Ok(result) = Context::<f64>::default()
                .evaluate(&display)
            else {
                *display = "error".to_string();
                return;
            };
            if let Some(result) = Decimal::from_f64(
                (result * 100.).round() / 100.,
            ) {
                *display = result.normalize().to_string();
            } else {
                *display = result.to_string();
            }
        }
        x => {
            display.push_str(x);
        }
    }

    for mut text in &mut text_query {
        text.sections[0].value =
            display.chars().tail(11).collect::<String>();
    }
}

#[derive(Component)]
struct Output;

fn startup(mut commands: Commands) {
    commands.spawn(Camera2dBundle::default());

    let container = commands
        .spawn(NodeBundle {
            style: Style {
                display: Display::Grid,
                grid_template_columns:
                    RepeatedGridTrack::auto(4),
                margin: UiRect::all(Val::Auto),
                ..default()
            },
            ..default()
        })
        .id();

    let items = vec![
        "C", "", "7", "8", "9", "/", "4", "5", "6", "*",
        "1", "2", "3", "-", "0", ".", "=", "+",
    ];

    for item in items {
        let button = commands
            .spawn(ButtonBundle {
                style: Style {
                    grid_column: GridPlacement::span(
                        if item == "" { 3 } else { 1 },
                    ),
                    border: UiRect::all(Val::Px(1.)),
                    padding: UiRect::all(Val::Px(20.)),
                    margin: UiRect::all(Val::Px(5.)),
                    ..default()
                },
                border_radius: BorderRadius::all(
                    if item == "" {
                        Val::Px(0.)
                    } else {
                        Val::Px(5.)
                    },
                ),
                border_color: BorderColor(if item == "" {
                    SKY_950.into()
                } else {
                    SLATE_400.into()
                }),
                ..default()
            })
            .with_children(|builder| {
                let mut b = builder.spawn(TextBundle {
                    text: Text::from_section(
                        item,
                        TextStyle::default(),
                    ),
                    ..default()
                });

                if item == "" {
                    b.insert(Output);
                }
            })
            .id();

        commands.entity(container).add_child(button);
    }
}

#[derive(Event)]
struct PressEvent(String);

const NORMAL_BUTTON: Srgba = SLATE_500;
const HOVERED_BUTTON: Srgba = SLATE_400;
const PRESSED_BUTTON: Srgba = GREEN_400;

fn button_system(
    mut commands: Commands,
    mut interaction_query: Query<
        (
            &Interaction,
            &mut BackgroundColor,
            &Children,
        ),
        (Changed<Interaction>, With<Button>),
    >,
    text_query: Query<&Text>,
) {
    for (interaction, mut color, children) in
        &mut interaction_query
    {
        let text = text_query.get(children[0]).unwrap();
        match *interaction {
            Interaction::Pressed => {
                *color = PRESSED_BUTTON.into();
                commands.trigger(PressEvent(
                    text.sections[0].value.to_string(),
                ));
            }
            Interaction::Hovered => {
                *color = HOVERED_BUTTON.into();
            }
            Interaction::None => {
                *color = NORMAL_BUTTON.into();
            }
        }
    }
}