//! Play-in-editor session: player bootstrap and viewport camera binding.

use bevy::camera::RenderTarget;
use bevy::prelude::*;
use bevy::window::WindowRef;
use game::player::PlayerCamera;
use protocol::PlayerInputIntent;
use settings::SimTuning;
use shared::{
    authoring_component_active, AuthoringComponentStates, InspectorOrder, PlayerSpawn,
    COMPONENT_PLAYER_SPAWN,
};
use sim::{Crouching, Grounded, JumpState, Player, PlayerVelocity};

use crate::render_target::ViewportRenderTarget;
use crate::state::{EditorMode, PlayPossession};

pub struct PlaySessionPlugin;

impl Plugin for PlaySessionPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(OnEnter(EditorMode::Playing), bootstrap_player_on_play)
            .add_systems(
                PostUpdate,
                sync_play_camera_target.after(crate::scene_view::ensure_viewport_render_target),
            );
    }
}

/// Default player spawn used when no [`PlayerSpawn`] marker exists in the scene.
pub fn default_player_spawn() -> Transform {
    Transform::from_xyz(0.0, 1.5, 10.0)
}

pub(crate) fn sync_play_camera_target(
    mode: Res<State<EditorMode>>,
    possession: Res<PlayPossession>,
    target: Res<ViewportRenderTarget>,
    mut player_cameras: Query<(Entity, &mut Camera), With<PlayerCamera>>,
    mut commands: Commands,
) {
    let possessed = *mode.get() == EditorMode::Playing && *possession == PlayPossession::Possessed;

    for (entity, mut camera) in &mut player_cameras {
        camera.viewport = None;

        if possessed {
            let Some(viewport_target) = target.0.as_ref() else {
                camera.is_active = false;
                continue;
            };
            camera.is_active = true;
            commands
                .entity(entity)
                .insert(RenderTarget::Image(viewport_target.image.clone().into()));
        } else {
            camera.is_active = false;
            commands
                .entity(entity)
                .insert(RenderTarget::Window(WindowRef::Primary));
        }
    }
}

fn bootstrap_player_on_play(world: &mut World) {
    let tuning = world
        .get_resource::<SimTuning>()
        .copied()
        .unwrap_or_else(|| SimTuning::from_physics(&settings::PhysicsSettings::default()));

    let spawn = world
        .query_filtered::<(
            &Transform,
            Option<&AuthoringComponentStates>,
            Option<&InspectorOrder>,
        ), With<PlayerSpawn>>()
        .iter(world)
        .filter(|(_, states, legacy_order)| {
            authoring_component_active(*states, *legacy_order, COMPONENT_PLAYER_SPAWN)
        })
        .map(|(transform, _, _)| transform)
        .next()
        .copied()
        .unwrap_or_else(default_player_spawn);

    let mut player_query = world.query_filtered::<(
        Entity,
        &mut Transform,
        &mut PlayerVelocity,
        &mut PlayerInputIntent,
        &mut JumpState,
        &mut Grounded,
        &mut Crouching,
    ), With<Player>>();
    let Some((
        player_entity,
        mut transform,
        mut velocity,
        mut intent,
        mut jump,
        mut grounded,
        mut crouching,
    )) = player_query.iter_mut(world).next()
    else {
        warn!("Play mode entered but no Player entity exists");
        return;
    };

    *transform = spawn;
    velocity.0 = Vec3::ZERO;
    *intent = PlayerInputIntent::default();
    *jump = JumpState::default();
    grounded.0 = false;
    crouching.0 = false;
    let position = transform.translation;

    {
        let mut camera_query = world.query_filtered::<&mut Transform, With<PlayerCamera>>();
        for mut camera_transform in camera_query.iter_mut(world) {
            camera_transform.translation = Vec3::new(0.0, tuning.eye_offset_stand, 0.0);
            camera_transform.rotation = Quat::IDENTITY;
        }
    }

    info!("Play session started: player {player_entity:?} at {position:?}");
}
