use super::add::{AddGameOptions, ResolvedGameDetails};
use super::add_discovery::{EmulatorPrefillResult, maybe_prefill_from_emulators};
use crate::game::config::{Game, GameInstallation, InstallationsConfig, InstantGameConfig};
use crate::game::launch_command::LaunchCommand;
use crate::game::utils::path::tilde_display_string;
use anyhow::{Context, Result};

pub(super) struct GameCreationContext {
    pub(super) config: InstantGameConfig,
    pub(super) installations: InstallationsConfig,
}

impl GameCreationContext {
    pub(super) fn load() -> Result<Self> {
        Ok(Self {
            config: InstantGameConfig::load().context("Failed to load game configuration")?,
            installations: InstallationsConfig::load()
                .context("Failed to load installations configuration")?,
        })
    }

    pub(super) fn game_exists(&self, name: &str) -> bool {
        self.config.games.iter().any(|g| g.name.0 == name)
    }

    pub(super) fn installation_exists(&self, name: &str) -> bool {
        self.installations
            .installations
            .iter()
            .any(|inst| inst.game_name.0 == name)
    }

    pub(super) fn save(&self) -> Result<()> {
        self.config.save()?;
        self.installations.save()?;
        Ok(())
    }
}

/// Manage game CRUD operations
pub struct GameManager;

impl GameManager {
    /// Add a new game to the configuration
    pub fn add_game(options: AddGameOptions) -> Result<()> {
        let mut context = GameCreationContext::load()?;

        if !super::validation::validate_game_manager_initialized()? {
            return Ok(());
        }

        if options.name.is_none() {
            loop {
                match maybe_prefill_from_emulators(options.clone(), &context)? {
                    EmulatorPrefillResult::OpenGameMenu(game_name) => {
                        return crate::game::menu::game_menu(Some(game_name));
                    }
                    EmulatorPrefillResult::SetupTrackedGame {
                        game_name,
                        discovered_save_path,
                    } => {
                        return crate::game::setup::setup_tracked_game(
                            &game_name,
                            discovered_save_path.as_deref(),
                        );
                    }
                    EmulatorPrefillResult::OpenPrefilledAddEditor(new_options) => {
                        return crate::game::menu::open_prefilled_add_editor(new_options);
                    }
                    EmulatorPrefillResult::Continue(new_options) => {
                        let Some(details) =
                            super::add::resolve_add_game_details(new_options, &context)?
                        else {
                            continue;
                        };
                        return Self::finish_add_game(&mut context, details);
                    }
                    EmulatorPrefillResult::Cancelled => {
                        return Ok(());
                    }
                }
            }
        }

        let Some(details) = super::add::resolve_add_game_details(options, &context)? else {
            return Ok(());
        };
        Self::finish_add_game(&mut context, details)
    }

    fn finish_add_game(
        context: &mut GameCreationContext,
        details: ResolvedGameDetails,
    ) -> Result<()> {
        let mut game = Game::new(details.name.clone());
        if let Some(description) = &details.description {
            game.description = Some(description.clone());
        }
        if let Some(command) = &details.launch_command {
            game.launch_command = Some(LaunchCommand::from_shell_or_manual(command.clone()));
        }

        context.config.games.push(game);

        context
            .installations
            .installations
            .push(GameInstallation::with_kind(
                details.name.clone(),
                details.save_path.clone(),
                details.save_path_type,
            ));

        context.save()?;

        let save_path_display = tilde_display_string(&details.save_path);

        println!("✓ Game '{}' added successfully!", details.name);
        println!(
            "Game configuration saved with save path: {}",
            save_path_display
        );

        Ok(())
    }

    /// Relocate a game's save path to point to a new location (does not move files)
    pub fn relocate_game(game_name: Option<String>, new_path: Option<String>) -> Result<()> {
        let mut context = GameCreationContext::load()?;

        let game_name = match super::relocate::resolve_relocation_game_name(game_name)? {
            Some(name) => name,
            None => return Ok(()),
        };

        if !super::relocate::ensure_game_exists(&context, &game_name)? {
            return Ok(());
        }

        let new_path = super::relocate::resolve_relocation_save_path(&game_name, new_path)?;
        let save_path_type = super::relocate::determine_save_path_type(&new_path)?;

        super::relocate::upsert_installation(
            &mut context.installations.installations,
            &game_name,
            new_path.clone(),
            save_path_type,
        );

        context.save()?;

        let path_display = tilde_display_string(&new_path);

        println!("✓ Save path for '{game_name}' relocated successfully!");
        println!("New save path: {}", path_display);

        Ok(())
    }
}
