use crate::prelude::KeyCode as WinitKeyCode;
use crate::prelude::{
    Atmosphere, ElementState, Entity, HudAnchor, State as NightshadeState, TextProperties, Vec2,
    Vec3, Vec4, spawn_camera, spawn_hud_text_with_properties,
};
use crate::tui::backend::grid_renderer::{CellGrid, build_grid_from_tui_world};
use crate::tui::backend::key_map;
use crate::tui::ecs::events::Message as TuiMessage;
use crate::tui::ecs::resources::MouseButton as TuiMouseButton;
use crate::tui::ecs::world::{self as tui_world, World as TuiWorld};
use crate::tui::run::State as TuiState;

const DEFAULT_FONT_SIZE: f32 = 16.0;
const MONOSPACE_WIDTH_FACTOR: f32 = 0.6;

pub struct NightshadeAdapter {
    tui_state: Box<dyn TuiState>,
    tui_world: TuiWorld,
    row_entities: Vec<Entity>,
    grid_columns: u16,
    grid_rows: u16,
    font_size: f32,
    cell_grid: CellGrid,
    initialized_grid: bool,
    prev_mouse_column: u16,
    prev_mouse_row: u16,
}

impl NightshadeAdapter {
    pub fn new(state: Box<dyn TuiState>) -> Self {
        Self {
            tui_state: state,
            tui_world: TuiWorld::default(),
            row_entities: Vec::new(),
            grid_columns: 0,
            grid_rows: 0,
            font_size: DEFAULT_FONT_SIZE,
            cell_grid: CellGrid::new(0, 0),
            initialized_grid: false,
            prev_mouse_column: 0,
            prev_mouse_row: 0,
        }
    }

    fn calculate_grid_dimensions(&self, window_width: u32, window_height: u32) -> (u16, u16) {
        let cell_width = self.font_size * MONOSPACE_WIDTH_FACTOR;
        let cell_height = self.font_size * 1.2;
        let columns = (window_width as f32 / cell_width).floor() as u16;
        let rows = (window_height as f32 / cell_height).floor() as u16;
        (columns.max(1), rows.max(1))
    }

    fn pixel_to_grid(&self, pixel_x: f32, pixel_y: f32) -> (u16, u16) {
        let cell_width = self.font_size * MONOSPACE_WIDTH_FACTOR;
        let cell_height = self.font_size * 1.2;
        let column = (pixel_x / cell_width).floor() as i32;
        let row = (pixel_y / cell_height).floor() as i32;
        let column = column.clamp(0, self.grid_columns.saturating_sub(1) as i32) as u16;
        let row = row.clamp(0, self.grid_rows.saturating_sub(1) as i32) as u16;
        (column, row)
    }

    fn spawn_row_entities(&mut self, nightshade_world: &mut crate::ecs::world::World) {
        if !self.row_entities.is_empty() {
            nightshade_world.despawn_entities(&self.row_entities);
            self.row_entities.clear();
        }

        let monospace_width = self.font_size * MONOSPACE_WIDTH_FACTOR;
        let line_height = 1.2;

        for row_index in 0..self.grid_rows {
            let row_string: String = " ".repeat(self.grid_columns as usize);
            let y_position = row_index as f32 * self.font_size * line_height;

            let entity = spawn_hud_text_with_properties(
                nightshade_world,
                row_string,
                HudAnchor::TopLeft,
                Vec2::new(0.0, y_position),
                TextProperties {
                    font_size: self.font_size,
                    color: Vec4::new(1.0, 1.0, 1.0, 1.0),
                    monospace_width: Some(monospace_width),
                    line_height,
                    ..Default::default()
                },
            );

            nightshade_world.queue_add_components(
                entity,
                crate::ecs::world::TEXT_CHARACTER_COLORS
                    | crate::ecs::world::TEXT_CHARACTER_BACKGROUND_COLORS,
            );
            nightshade_world.apply_commands();

            if let Some(char_colors) = nightshade_world.get_text_character_colors_mut(entity) {
                char_colors.colors = vec![None; self.grid_columns as usize];
                char_colors.dirty = true;
            }

            if let Some(bg_colors) =
                nightshade_world.get_text_character_background_colors_mut(entity)
            {
                bg_colors.colors = vec![None; self.grid_columns as usize];
                bg_colors.dirty = true;
            }

            self.row_entities.push(entity);
        }
    }

    fn update_hud_rows(&mut self, nightshade_world: &mut crate::ecs::world::World) {
        for row_index in 0..self.grid_rows {
            let entity = self.row_entities[row_index as usize];

            let mut row_string = String::with_capacity(self.grid_columns as usize);
            let mut row_colors: Vec<Option<Vec4>> = Vec::with_capacity(self.grid_columns as usize);
            let mut row_bg_colors: Vec<Option<Vec4>> =
                Vec::with_capacity(self.grid_columns as usize);

            for column_index in 0..self.grid_columns {
                let cell = self.cell_grid.get(column_index, row_index);
                row_string.push(cell.character);
                let [r, g, b, a] = cell.foreground.to_vec4();
                row_colors.push(Some(Vec4::new(r, g, b, a)));
                let [br, bg, bb, ba] = cell.background.to_vec4();
                row_bg_colors.push(Some(Vec4::new(br, bg, bb, ba)));
            }

            if let Some(hud_text) = nightshade_world.get_hud_text(entity) {
                let text_index = hud_text.text_index;
                nightshade_world
                    .resources
                    .text_cache
                    .set_text(text_index, row_string);
            }

            if let Some(hud_text) = nightshade_world.get_hud_text_mut(entity) {
                hud_text.dirty = true;
            }

            if let Some(char_colors) = nightshade_world.get_text_character_colors_mut(entity) {
                char_colors.colors = row_colors;
                char_colors.dirty = true;
            }

            if let Some(bg_colors) =
                nightshade_world.get_text_character_background_colors_mut(entity)
            {
                bg_colors.colors = row_bg_colors;
                bg_colors.dirty = true;
            }
        }
    }
}

impl NightshadeState for NightshadeAdapter {
    fn title(&self) -> &str {
        self.tui_state.title()
    }

    fn initialize(&mut self, world: &mut crate::ecs::world::World) {
        world.resources.user_interface.enabled = false;
        world.resources.graphics.atmosphere = Atmosphere::None;
        world.resources.graphics.clear_color = [0.0, 0.0, 0.0, 1.0];
        world.resources.graphics.show_grid = false;

        let camera = spawn_camera(world, Vec3::new(0.0, 0.0, 10.0), "TUI Camera".to_string());
        world.resources.active_camera = Some(camera);

        let (window_width, window_height) = if let Some(handle) = &world.resources.window.handle {
            let size = handle.inner_size();
            (size.width, size.height)
        } else {
            (800, 600)
        };

        let (columns, rows) = self.calculate_grid_dimensions(window_width, window_height);
        self.grid_columns = columns;
        self.grid_rows = rows;
        self.cell_grid = CellGrid::new(columns, rows);

        self.spawn_row_entities(world);

        self.tui_world.resources.terminal_size.columns = columns;
        self.tui_world.resources.terminal_size.rows = rows;

        self.tui_state.initialize(&mut self.tui_world);
        self.initialized_grid = true;
    }

    fn on_keyboard_input(
        &mut self,
        world: &mut crate::ecs::world::World,
        winit_key: WinitKeyCode,
        key_state: ElementState,
    ) {
        let shift = world
            .resources
            .input
            .keyboard
            .is_key_pressed(WinitKeyCode::ShiftLeft)
            || world
                .resources
                .input
                .keyboard
                .is_key_pressed(WinitKeyCode::ShiftRight);

        let Some(tui_key) = key_map::from_winit(winit_key, shift) else {
            return;
        };

        let pressed = key_state == ElementState::Pressed;

        if pressed {
            self.tui_world.resources.keyboard.pressed.insert(tui_key);
            self.tui_world
                .resources
                .keyboard
                .just_pressed
                .insert(tui_key);
        } else {
            self.tui_world.resources.keyboard.pressed.remove(&tui_key);
            self.tui_world
                .resources
                .keyboard
                .just_released
                .insert(tui_key);
        }

        self.tui_state
            .on_keyboard_input(&mut self.tui_world, tui_key, pressed);
    }

    fn on_mouse_input(
        &mut self,
        world: &mut crate::ecs::world::World,
        state: winit::event::ElementState,
        button: winit::event::MouseButton,
    ) {
        let tui_button = match button {
            winit::event::MouseButton::Left => TuiMouseButton::Left,
            winit::event::MouseButton::Right => TuiMouseButton::Right,
            winit::event::MouseButton::Middle => TuiMouseButton::Middle,
            _ => return,
        };

        let pressed = state == ElementState::Pressed;
        let pixel_position = world.resources.input.mouse.position;
        let (column, row) = self.pixel_to_grid(pixel_position.x, pixel_position.y);

        self.tui_world.resources.mouse.column = column;
        self.tui_world.resources.mouse.row = row;

        match tui_button {
            TuiMouseButton::Left => {
                self.tui_world.resources.mouse.left_pressed = pressed;
                if pressed {
                    self.tui_world.resources.mouse.left_just_pressed = true;
                } else {
                    self.tui_world.resources.mouse.left_just_released = true;
                }
            }
            TuiMouseButton::Right => {
                self.tui_world.resources.mouse.right_pressed = pressed;
                if pressed {
                    self.tui_world.resources.mouse.right_just_pressed = true;
                } else {
                    self.tui_world.resources.mouse.right_just_released = true;
                }
            }
            TuiMouseButton::Middle => {
                self.tui_world.resources.mouse.middle_pressed = pressed;
                if pressed {
                    self.tui_world.resources.mouse.middle_just_pressed = true;
                } else {
                    self.tui_world.resources.mouse.middle_just_released = true;
                }
            }
        }

        self.tui_state
            .on_mouse_input(&mut self.tui_world, tui_button, column, row, pressed);
    }

    fn run_systems(&mut self, world: &mut crate::ecs::world::World) {
        if !self.initialized_grid {
            return;
        }

        let (window_width, window_height) = if let Some(handle) = &world.resources.window.handle {
            let size = handle.inner_size();
            (size.width, size.height)
        } else {
            world
                .resources
                .window
                .cached_viewport_size
                .unwrap_or((800, 600))
        };

        let (new_columns, new_rows) = self.calculate_grid_dimensions(window_width, window_height);
        if new_columns != self.grid_columns || new_rows != self.grid_rows {
            self.grid_columns = new_columns;
            self.grid_rows = new_rows;
            self.cell_grid = CellGrid::new(new_columns, new_rows);
            self.spawn_row_entities(world);
            self.tui_world.resources.terminal_size.columns = new_columns;
            self.tui_world.resources.terminal_size.rows = new_rows;
        }

        self.tui_world.resources.timing.delta_seconds =
            world.resources.window.timing.delta_time as f64;
        self.tui_world.resources.timing.elapsed += self.tui_world.resources.timing.delta_seconds;
        self.tui_world.resources.timing.frame_count += 1;

        let pixel_position = world.resources.input.mouse.position;
        let (current_column, current_row) = self.pixel_to_grid(pixel_position.x, pixel_position.y);
        self.tui_world.resources.mouse.column = current_column;
        self.tui_world.resources.mouse.row = current_row;
        if current_column != self.prev_mouse_column || current_row != self.prev_mouse_row {
            self.prev_mouse_column = current_column;
            self.prev_mouse_row = current_row;
            self.tui_state
                .on_mouse_move(&mut self.tui_world, current_column, current_row);
        }

        self.tui_state.run_systems(&mut self.tui_world);

        let messages: Vec<TuiMessage> = self
            .tui_world
            .resources
            .event_bus
            .messages
            .drain(..)
            .collect();
        for message in &messages {
            self.tui_state.handle_event(&mut self.tui_world, message);
        }

        tui_world::apply_commands(&mut self.tui_world);

        if let Some(next) = self.tui_state.next_state(&mut self.tui_world) {
            self.tui_state = next;
            self.tui_state.initialize(&mut self.tui_world);
        }

        build_grid_from_tui_world(&self.tui_world, &mut self.cell_grid);
        self.update_hud_rows(world);

        crate::ecs::text::systems::sync_hud_text_system(world);

        if self.tui_world.resources.should_exit {
            world.resources.window.should_exit = true;
        }

        self.tui_world.resources.keyboard.clear_frame();
        self.tui_world.resources.mouse.clear_frame();
    }
}
