// src/engine/app.rs

use anyhow::Result;
use std::sync::Arc;
use std::time::{Duration, Instant};

use winit::{
    application::ApplicationHandler,
    dpi::PhysicalSize,
    event::{ElementState, MouseButton, WindowEvent},
    event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
    keyboard::{KeyCode, PhysicalKey},
    window::Window,
};

use crate::engine::Engine;
use crate::engine::camera::controller::{
    ActiveCameraController, orbit::OrbitCameraController, pan_zoom::PanZoomCameraController,
};
use crate::engine::config::RenderConfig;
use crate::engine::export::{ExportSettings, export_scene};
use crate::engine::render::RenderOptions;
use crate::engine::scene::Scene;
use crate::frontend::theme::Theme;

pub struct App {
    window: Option<Arc<Window>>,
    engine: Option<Engine>,
    pending_scene: Option<Scene>,
    explicit_export_settings: Option<ExportSettings>,
    render_options: RenderOptions,
    preview_dt: f32,
    preview_frame_duration: Duration,
    preview_start_time: Option<Instant>,
    preview_frame_count: u64,
    camera_controller: ActiveCameraController,
    is_left_mouse_down: bool,
    last_cursor_position: Option<(f64, f64)>,
}

impl App {
    pub fn new() -> Result<Self> {
        let preview_dt = 1.0 / RenderConfig::preview()?.fps.max(1) as f32;
        Ok(Self {
            preview_dt,
            preview_frame_duration: Duration::from_secs_f32(preview_dt),
            preview_start_time: None,
            preview_frame_count: 0,
            window: None,
            engine: None,
            pending_scene: None,
            explicit_export_settings: None,
            render_options: RenderOptions::default(),
            camera_controller: ActiveCameraController::Orbit(OrbitCameraController::new(10.0)),
            is_left_mouse_down: false,
            last_cursor_position: None,
        })
    }

    pub fn run_app(mut self) -> Result<()> {
        let args: Vec<String> = std::env::args().collect();
        if should_preview(&args, &self.render_options) {
            print_camera_help();
            let event_loop = EventLoop::new()?;
            return event_loop
                .run_app(&mut self)
                .map_err(|e| anyhow::anyhow!(e));
        }

        let scene = self.pending_scene.take().unwrap_or_else(Scene::new);
        let settings = match self.explicit_export_settings.take() {
            Some(settings) => settings,
            None => ExportSettings::from_project_config(&scene, &self.render_options)?,
        };
        export_scene(scene, &settings)
    }

    pub fn with_render_options(mut self, options: RenderOptions) -> Self {
        self.render_options = options;
        self
    }

    pub fn with_preview(mut self) -> Self {
        self.render_options.video = Some(false);
        self
    }

    pub fn with_video_export(mut self) -> Self {
        self.render_options.video = Some(true);
        self
    }

    pub fn with_frames_export(mut self, enabled: bool) -> Self {
        self.render_options.frames = Some(enabled);
        self
    }

    pub fn with_export_settings(mut self, settings: ExportSettings) -> Self {
        self.explicit_export_settings = Some(settings);
        self
    }

    pub fn with_scene(mut self, scene: Scene) -> Self {
        self.pending_scene = Some(scene);
        self
    }
}

impl<'a> ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        if self.window.is_some() {
            return;
        }

        let window = event_loop
            .create_window(
                Window::default_attributes()
                    .with_title("Murali")
                    .with_inner_size(PhysicalSize::new(1280, 720)),
            )
            .expect("Failed to create window");

        let arc_window = Arc::new(window);

        let scene = self.pending_scene.take().unwrap_or_else(Scene::new);

        let mut engine =
            pollster::block_on(async { Engine::new_with_scene(arc_window.clone(), scene).await });

        // let bg = Theme::global().background;
        engine.set_clear_color(Theme::global().background);

        self.window = Some(arc_window.clone());
        self.engine = Some(engine);
        self.preview_start_time = Some(Instant::now());
        self.preview_frame_count = 0;
        event_loop.set_control_flow(ControlFlow::Poll);
        arc_window.request_redraw();
    }

    fn window_event(
        &mut self,
        event_loop: &ActiveEventLoop,
        _id: winit::window::WindowId,
        event: WindowEvent,
    ) {
        let Some(window) = self.window.as_ref() else {
            return;
        };
        let Some(engine) = self.engine.as_mut() else {
            return;
        };

        match event {
            WindowEvent::CloseRequested => event_loop.exit(),

            WindowEvent::RedrawRequested => {
                let Some(start_time) = self.preview_start_time else {
                    return;
                };

                // Drive the engine
                engine.update(self.preview_dt);
                self.preview_frame_count += 1;

                if let Err(e) = engine.render() {
                    eprintln!("Render error: {:?}", e);
                    event_loop.exit();
                    return;
                }

                let target_elapsed = self
                    .preview_frame_duration
                    .mul_f64(self.preview_frame_count as f64);
                let actual_elapsed = start_time.elapsed();

                if actual_elapsed < target_elapsed {
                    event_loop
                        .set_control_flow(ControlFlow::WaitUntil(start_time + target_elapsed));
                } else {
                    event_loop.set_control_flow(ControlFlow::Poll);
                }

                window.request_redraw();
            }

            WindowEvent::Resized(size) => {
                let corrected = enforce_16_9(size);
                if corrected != size {
                    let _ = window.request_inner_size(corrected);
                }
                // engine.backend.renderer.device_mgr.resize(corrected);
                engine.backend.renderer.resize(corrected);
            }

            WindowEvent::KeyboardInput { event, .. } if event.state == ElementState::Pressed => {
                match event.physical_key {
                    PhysicalKey::Code(KeyCode::KeyO) => {
                        self.camera_controller =
                            ActiveCameraController::Orbit(OrbitCameraController::new(10.0))
                    }
                    PhysicalKey::Code(KeyCode::KeyP) => {
                        self.camera_controller =
                            ActiveCameraController::PanZoom(PanZoomCameraController::new())
                    }
                    PhysicalKey::Code(KeyCode::Escape) => event_loop.exit(),
                    _ => {}
                }
            }

            WindowEvent::CursorMoved { position, .. } => {
                if let Some((lx, ly)) = self.last_cursor_position {
                    if self.is_left_mouse_down {
                        let delta = glam::vec2((position.x - lx) as f32, (position.y - ly) as f32);
                        self.camera_controller
                            .handle_mouse_drag(delta, engine.scene.camera_mut());
                    }
                }
                self.last_cursor_position = Some((position.x, position.y));
            }

            WindowEvent::MouseInput { state, button, .. } => {
                if button == MouseButton::Left {
                    self.is_left_mouse_down = state == ElementState::Pressed;
                }
            }

            WindowEvent::MouseWheel { delta, .. } => {
                let scroll = match delta {
                    winit::event::MouseScrollDelta::LineDelta(_, y) => y,
                    winit::event::MouseScrollDelta::PixelDelta(p) => p.y as f32 * 0.01,
                };
                self.camera_controller
                    .handle_scroll(scroll, engine.scene.camera_mut());
            }

            _ => {}
        }
    }
}

fn enforce_16_9(size: PhysicalSize<u32>) -> PhysicalSize<u32> {
    let ratio = 16.0 / 9.0;
    let w = size.width as f32;
    let h = size.height as f32;
    if (w / h) > ratio {
        PhysicalSize::new((h * ratio) as u32, size.height)
    } else {
        PhysicalSize::new(size.width, (w / ratio) as u32)
    }
}

fn print_camera_help() {
    println!("\n🎥 Controls: [O] Orbit | [P] PanZoom | [Drag] Move | [Wheel] Zoom\n");
}

fn should_preview(args: &[String], options: &RenderOptions) -> bool {
    if args.iter().any(|arg| arg == "--preview") {
        return true;
    }
    if args.iter().any(|arg| arg == "--export") {
        return false;
    }
    !options.video_enabled()
}
