use crate::core::SortField;
use crate::model::config::TransportMode;
use anyhow::Result;
use crossterm::{
    cursor::{Hide, Show},
    event::{DisableMouseCapture, EnableMouseCapture},
    execute,
    terminal::{
        EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode, size,
    },
};
use std::{io, path::PathBuf, time::Duration};

use super::ConfigOption;
use super::state::{RedrawMode, ViewerState, Viewport};

mod event_dispatch;
mod loop_control;
mod loop_support;
mod render;
mod worker;

use loop_control::{
    LoopPhaseContext, LoopStep, handle_pending_replace_phase, handle_polled_event_phase,
    maybe_run_idle_prefetch,
};
use loop_support::{
    LoopRenderContext, PendingPreviewState, PreviewSubmitParams, drain_preview_responses,
    handle_image_replace_redraw, handle_refresh_redraw, maybe_render_loading_indicator,
    request_preview_and_render_pending,
};
use render::render_current_mode;
use worker::spawn_preview_worker;

#[derive(Debug, Clone, Copy)]
struct RenderModeFlags {
    refresh_image: bool,
    full_refresh: bool,
    prefetch_after: bool,
}

/// ビューワーを実行する関数
pub fn run_viewer(
    stdout: &mut io::Stdout,
    image_files: &[PathBuf],
    start_index: usize,
    options: ConfigOption,
) -> Result<()> {
    if image_files.is_empty() {
        return Ok(());
    }

    enable_raw_mode()?;
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture, Hide)?;

    let mut current_index = start_index;
    let result = viewer_loop(stdout, image_files, &mut current_index, options);

    execute!(stdout, Show, DisableMouseCapture, LeaveAlternateScreen)?;
    disable_raw_mode()?;

    result
}

/// ビューワーのメインループを実行する関数
fn viewer_loop(
    stdout: &mut io::Stdout,
    image_files: &[PathBuf],
    current_index: &mut usize,
    options: ConfigOption,
) -> Result<()> {
    use super::render::image::ImageRenderState;
    use super::render::{HeaderRenderInput, filetree::SidebarTree};
    use super::runtime::ImageCache;
    use super::state::{
        CacheState, ImageProcessingConfig, NavDirection, PerformanceStats, PreviewState,
    };

    let mut image_files = image_files.to_vec();
    let (preview_req_tx, preview_resp_rx, preview_join) = spawn_preview_worker();
    let (initial_width, initial_height) = size()?;
    let mut viewport = Viewport {
        width: initial_width,
        height: initial_height,
    };
    let mut redraw_mode = RedrawMode::HeaderRefresh;
    let debounce_duration = Duration::from_millis(options.preview_debounce);
    let idle_poll_interval = Duration::from_millis(options.poll_interval);
    let idle_prefetch_interval = Duration::from_millis(options.prefetch_interval);
    let mut sort_field = SortField::Natural;
    let mut sort_descending = false;
    let mut pending_preview = PendingPreviewState::default();
    let mut state = ViewerState {
        pending_replace: false,
        pending_deadline: None,
        ui_state: super::state::UiState {
            sidebar_visible: options.sidebar_visible,
            sidebar_size: options.sidebar_size,
            header_visible: options.header_visible,
            statusbar_visible: options.statusbar_visible,
            overlay_visible: false,
            header_bg_color: options.header_bg_color,
            header_fg_color: options.header_fg_color,
            statusbar_bg_color: options.statusbar_bg_color,
            statusbar_fg_color: options.statusbar_fg_color,
        },
        cache: CacheState {
            image_cache: ImageCache::new(options.cache_lru_size, options.cache_max_bytes),
            entries: std::collections::HashMap::new(),
            next_kitty_id: 1,
        },
        preview: PreviewState {
            prefetch_size: options.prefetch_size,
            last_prefetch_state: None,
            preview_generation: 0,
            expected_preview_generation: None,
            last_idle_prefetch_at: None,
        },
        image_config: ImageProcessingConfig {
            image_diff_mode: options.image_diff_mode,
            transport_mode: options.transport_mode,
            dirty_ratio: options.dirty_ratio.clamp(0.0, 1.0),
            tile_grid: options.tile_grid.max(1),
            skip_step: options.skip_step,
            image_width: options.image_width,
            image_height: options.image_height,
            image_filter_type: options.image_filter_type,
            zoom_factor: 1.0,
            pan_x: 0,
            pan_y: 0,
        },
        perf: PerformanceStats::default(),
        sidebar_tree: SidebarTree::from_image_files(
            &image_files,
            *current_index,
            &options.image_extensions,
        ),
        image_render_state: ImageRenderState::new(),
        last_nav_direction: NavDirection::Forward,
    };

    let initial_diff_mode = state.image_config.image_diff_mode;
    let _ = request_preview_and_render_pending(
        &mut LoopRenderContext {
            stdout,
            image_files: &image_files,
            current_index: *current_index,
            viewport: &viewport,
            state: &mut state,
        },
        &preview_req_tx,
        &mut pending_preview,
        PreviewSubmitParams {
            diff_mode: initial_diff_mode,
            force_submit: false,
            flags: RenderModeFlags {
                refresh_image: false,
                full_refresh: false,
                prefetch_after: false,
            },
            force_refresh_on_replace: false,
        },
    )?;

    loop {
        drain_preview_responses(
            &mut LoopRenderContext {
                stdout,
                image_files: &image_files,
                current_index: *current_index,
                viewport: &viewport,
                state: &mut state,
            },
            &preview_resp_rx,
            &mut redraw_mode,
            &mut pending_preview,
        )?;

        maybe_render_loading_indicator(
            &mut LoopRenderContext {
                stdout,
                image_files: &image_files,
                current_index: *current_index,
                viewport: &viewport,
                state: &mut state,
            },
            &mut pending_preview,
        )?;

        match redraw_mode {
            RedrawMode::Idle => {}
            RedrawMode::HeaderRefresh => {
                if state.ui_state.overlay_visible
                    && state.image_config.transport_mode != TransportMode::File
                {
                    render_current_mode(
                        stdout,
                        &image_files,
                        *current_index,
                        &viewport,
                        &mut state,
                        RenderModeFlags {
                            refresh_image: false,
                            full_refresh: false,
                            prefetch_after: false,
                        },
                    )?;
                    redraw_mode = RedrawMode::Idle;
                    continue;
                }

                let sidebar_entries = state
                    .sidebar_tree
                    .render_entries(image_files.get(*current_index));
                super::render::render_header_only(
                    stdout,
                    HeaderRenderInput {
                        image_files: &image_files,
                        current_index: *current_index,
                        sidebar_entries: &sidebar_entries,
                        term_width: viewport.width,
                        term_height: viewport.height,
                        sidebar_visible: state.ui_state.sidebar_visible,
                        header_visible: state.ui_state.header_visible,
                        sidebar_size: state.ui_state.sidebar_size,
                        header_bg_color: state.ui_state.header_bg_color,
                        header_fg_color: state.ui_state.header_fg_color,
                    },
                )?;
                redraw_mode = RedrawMode::Idle;
            }
            RedrawMode::FullRefresh => {
                handle_refresh_redraw(
                    &mut LoopRenderContext {
                        stdout,
                        image_files: &image_files,
                        current_index: *current_index,
                        viewport: &viewport,
                        state: &mut state,
                    },
                    &preview_req_tx,
                    RenderModeFlags {
                        refresh_image: true,
                        full_refresh: true,
                        prefetch_after: false,
                    },
                    &mut pending_preview,
                )?;
                redraw_mode = RedrawMode::Idle;
            }
            RedrawMode::LayoutRefresh => {
                handle_refresh_redraw(
                    &mut LoopRenderContext {
                        stdout,
                        image_files: &image_files,
                        current_index: *current_index,
                        viewport: &viewport,
                        state: &mut state,
                    },
                    &preview_req_tx,
                    RenderModeFlags {
                        refresh_image: true,
                        full_refresh: true,
                        prefetch_after: false,
                    },
                    &mut pending_preview,
                )?;
                redraw_mode = RedrawMode::Idle;
            }
            RedrawMode::ImageRefresh => {
                handle_refresh_redraw(
                    &mut LoopRenderContext {
                        stdout,
                        image_files: &image_files,
                        current_index: *current_index,
                        viewport: &viewport,
                        state: &mut state,
                    },
                    &preview_req_tx,
                    RenderModeFlags {
                        refresh_image: true,
                        full_refresh: false,
                        prefetch_after: false,
                    },
                    &mut pending_preview,
                )?;
                redraw_mode = RedrawMode::Idle;
            }
            RedrawMode::ImageReplace => {
                handle_image_replace_redraw(
                    &mut LoopRenderContext {
                        stdout,
                        image_files: &image_files,
                        current_index: *current_index,
                        viewport: &viewport,
                        state: &mut state,
                    },
                    &mut pending_preview,
                )?;
                redraw_mode = RedrawMode::Idle;
            }
        }

        match handle_pending_replace_phase(&mut LoopPhaseContext {
            stdout,
            image_files: &mut image_files,
            sort_field: &mut sort_field,
            sort_descending: &mut sort_descending,
            current_index,
            redraw_mode: &mut redraw_mode,
            state: &mut state,
            viewport: &mut viewport,
            debounce_duration,
            preview_req_tx: &preview_req_tx,
            pending_preview: &mut pending_preview,
        })? {
            LoopStep::Break => break,
            LoopStep::Continue => continue,
            LoopStep::NotHandled => {}
        }

        match handle_polled_event_phase(
            &mut LoopPhaseContext {
                stdout,
                image_files: &mut image_files,
                sort_field: &mut sort_field,
                sort_descending: &mut sort_descending,
                current_index,
                redraw_mode: &mut redraw_mode,
                state: &mut state,
                viewport: &mut viewport,
                debounce_duration,
                preview_req_tx: &preview_req_tx,
                pending_preview: &mut pending_preview,
            },
            idle_poll_interval,
        )? {
            LoopStep::Break => break,
            LoopStep::Continue => {}
            LoopStep::NotHandled => {
                maybe_run_idle_prefetch(
                    &image_files,
                    *current_index,
                    &mut state,
                    redraw_mode,
                    idle_prefetch_interval,
                );
            }
        }
    }

    drop(preview_req_tx);
    let _ = preview_join.join();

    Ok(())
}
