//! Seperate out debugging uis/plugins in it's own module for cleanliness.
use std::{fs, path::PathBuf};

use bevy::prelude::*;
use bevy_inspector_egui::bevy_egui::EguiPlugin;
use clap::Subcommand;
use console::{ConsoleLog, exec_script};

use crate::AppState;

mod console;

/// Debug system set.
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub struct DebugSet;

pub fn plugin(app: &mut App) {
    app.add_plugins(EguiPlugin {
        enable_multipass_for_primary_context: true,
    })
    .add_event::<DebugEvent>()
    .add_systems(
        Update,
        (handle_debug_logs, start_game, run_file.pipe(exec_script)).in_set(DebugSet),
    )
    .add_systems(Startup, startup_file.pipe(exec_script))
    .add_plugins(console::plugin);
}

#[derive(Event, Debug, Subcommand, PartialEq, Eq)]
enum DebugEvent {
    /// Close the debug console.
    #[command(name = "close", aliases = ["close-console"])]
    CloseDebugConsole,

    /// Output a string to the console.
    ///
    /// Needed for logging in startup scripts, since the console isn't initialized yet.
    #[command(name = "echo", aliases = ["print", "print-to-console"])]
    PrintToConsole {
        /// Print as error.
        #[arg(short, long)]
        error: bool,
        /// The text to be printed in the console.
        text: String,
    },

    /// Log as INFO or ERROR to stdout.
    Log {
        /// Log as error.
        #[arg(short, long)]
        error: bool,
        /// The text to be logged.
        text: String,
    },

    /// Start the game
    StartGame,

    /// Run a tx file (the games debug scripting lang :3)
    #[command(name = "run")]
    RunFile { file: PathBuf },
}

fn start_game(
    mut dbg_reader: EventReader<DebugEvent>,
    mut log: ResMut<ConsoleLog>,
    cur_state: Res<State<AppState>>,
    mut next_state: ResMut<NextState<AppState>>,
) {
    for ev in dbg_reader.read() {
        if *ev == DebugEvent::StartGame {
            if *cur_state == AppState::Ingame {
                log.err("Can't start game since it's already started.");
            } else {
                log.write("Starting game...");
                next_state.set(AppState::Ingame);
            }
        }
    }
}

fn handle_debug_logs(mut dbg_reader: EventReader<DebugEvent>) {
    for ev in dbg_reader.read() {
        if let DebugEvent::Log { error, text } = ev {
            if *error {
                error!("{text}")
            } else {
                info!("{text}")
            };
        }
    }
}

fn run_file(mut dbg_reader: EventReader<DebugEvent>) -> Option<(String, String)> {
    for ev in dbg_reader.read() {
        if let DebugEvent::RunFile { file } = ev {
            let f = std::fs::read_to_string(file);

            if let Ok(f) = f {
                return Some((f, String::from(file.file_name().unwrap().to_string_lossy())));
            }
        }
    }

    None
}

fn startup_file() -> Option<(String, String)> {
    let path: PathBuf = "./startup.tx".into();

    fs::exists(&path).unwrap_or(false).then(|| {
        Some((
            fs::read_to_string(&path).unwrap(),
            path.file_name().unwrap().to_string_lossy().to_string(),
        ))
    })?
}
