use crate::client::tui::app::{App, ConfirmAction, Focus, MessageRole, Modal};
use crate::protocol::{MessageAttachment, UploadDescriptor, WsInbound, serialize_inbound};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use futures_util::SinkExt;
use tokio::io::AsyncWriteExt;
use tokio_tungstenite::tungstenite::Message;
use tokio_util::io::ReaderStream;

pub async fn handle_key_event(app: &mut App, key: KeyEvent) {
    if app.show_help {
        if matches!(key.code, KeyCode::Esc | KeyCode::F(1))
            || (key.code == KeyCode::Char('q') && key.modifiers.is_empty())
        {
            app.show_help = false;
        }
        return;
    }

    if app.modal.is_some() {
        handle_modal_key(app, key).await;
        return;
    }

    let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
    if ctrl && key.code == KeyCode::Char('c') {
        app.handle_ctrl_c_for_quit();
        return;
    }
    app.cancel_pending_quit();

    if matches!(key.code, KeyCode::F(1)) || (ctrl && key.code == KeyCode::Char('h')) {
        app.show_help = true;
        return;
    }

    if app.show_command_menu {
        match key.code {
            KeyCode::Esc => close_command_menu(app),
            KeyCode::Up => app.select_previous_command(),
            KeyCode::Down => app.select_next_command(),
            KeyCode::Tab => {
                app.insert_selected_command();
                close_command_menu(app);
            }
            KeyCode::Enter if key.modifiers.is_empty() => {
                app.insert_selected_command();
                close_command_menu(app);
            }
            _ => handle_input_key(app, key).await,
        }
        return;
    }

    if ctrl {
        match key.code {
            KeyCode::Char('n') => {
                send(app, WsInbound::CreateSession { title: None }).await;
                app.status_message = Some("正在创建会话…".to_string());
            }
            KeyCode::Char('s') => app.focus = Focus::Sessions,
            KeyCode::Char('r') => open_rename(app),
            KeyCode::Char('a') => app.modal = Some(Modal::Confirm(ConfirmAction::Archive)),
            KeyCode::Char('d') => app.modal = Some(Modal::Confirm(ConfirmAction::Delete)),
            KeyCode::Char('l') => app.modal = Some(Modal::Confirm(ConfirmAction::ClearHistory)),
            KeyCode::Char('o') => {
                app.show_archived = !app.show_archived;
                request_session_list(app).await;
            }
            KeyCode::Char('f') => {
                if app.file_transfer_supported {
                    app.modal = Some(Modal::AttachPath {
                        input: String::new(),
                        cursor: 0,
                    });
                } else {
                    app.status_message = Some("当前 Gateway 不支持文件传输".to_string());
                }
            }
            KeyCode::Char('u') if app.focus == Focus::Input => {
                app.input.clear();
                app.input_cursor_pos = 0;
            }
            KeyCode::Char('x') if app.focus == Focus::Input => {
                app.pending_uploads.clear();
                app.status_message = Some("已移除待发送附件".to_string());
            }
            KeyCode::Up => app.scroll_chat_up(3),
            KeyCode::Down => app.scroll_chat_down(3),
            _ => {}
        }
        return;
    }

    match key.code {
        KeyCode::Tab => {
            app.focus = match app.focus {
                Focus::Input => Focus::Sessions,
                Focus::Sessions => Focus::Input,
            };
        }
        KeyCode::Esc => app.focus = Focus::Input,
        KeyCode::PageUp => app.scroll_chat_up(10),
        KeyCode::PageDown => app.scroll_chat_down(10),
        KeyCode::F(2) => download_latest_attachment(app).await,
        KeyCode::Home if app.focus == Focus::Sessions => app.selected_session = 0,
        KeyCode::End if app.focus == Focus::Sessions => {
            app.selected_session = app.sessions.len().saturating_sub(1);
        }
        _ if app.focus == Focus::Sessions => handle_session_key(app, key).await,
        _ => handle_input_key(app, key).await,
    }
}

pub fn handle_paste(app: &mut App, text: &str) {
    if let Some(Modal::Rename { input, cursor } | Modal::AttachPath { input, cursor }) =
        &mut app.modal
    {
        let remaining = 256_usize.saturating_sub(input.len());
        let mut end = text.len().min(remaining);
        while !text.is_char_boundary(end) {
            end -= 1;
        }
        input.insert_str(*cursor, &text[..end]);
        *cursor += end;
    } else if app.focus == Focus::Input {
        app.input_insert_str(text);
        update_command_menu(app);
    }
}

async fn handle_session_key(app: &mut App, key: KeyEvent) {
    match key.code {
        KeyCode::Up | KeyCode::Char('k') => app.select_previous_session(),
        KeyCode::Down | KeyCode::Char('j') => app.select_next_session(),
        KeyCode::Enter => {
            if let Some(session_id) = app.selected_session_id()
                && app.current_session_id.as_deref() != Some(&session_id)
            {
                app.status_message = Some("正在载入会话…".to_string());
                send(app, WsInbound::LoadSession { session_id }).await;
            }
        }
        KeyCode::Char('n') => {
            send(app, WsInbound::CreateSession { title: None }).await;
        }
        KeyCode::Char('r') => open_rename(app),
        KeyCode::Char('a') => app.modal = Some(Modal::Confirm(ConfirmAction::Archive)),
        KeyCode::Char('d') => app.modal = Some(Modal::Confirm(ConfirmAction::Delete)),
        _ => {}
    }
}

async fn handle_input_key(app: &mut App, key: KeyEvent) {
    match key.code {
        KeyCode::Char(c) => {
            app.input_insert_char(c);
            update_command_menu(app);
        }
        KeyCode::Backspace => {
            app.input_delete_char();
            update_command_menu(app);
        }
        KeyCode::Delete => app.input_delete_forward(),
        KeyCode::Left => app.input_move_cursor_left(),
        KeyCode::Right => app.input_move_cursor_right(),
        KeyCode::Home => app.input_move_cursor_to_start(),
        KeyCode::End => app.input_move_cursor_to_end(),
        KeyCode::Up => app.scroll_chat_up(1),
        KeyCode::Down => app.scroll_chat_down(1),
        KeyCode::Enter
            if key.modifiers.contains(KeyModifiers::SHIFT)
                || key.modifiers.contains(KeyModifiers::ALT) =>
        {
            app.input_insert_char('\n');
        }
        KeyCode::Enter => {
            let input = app.take_input();
            close_command_menu(app);
            if !input.trim().is_empty() || !app.pending_uploads.is_empty() {
                let upload_ids = app
                    .pending_uploads
                    .iter()
                    .map(|upload| upload.upload_id.clone())
                    .collect::<Vec<_>>();
                let attachments = app
                    .pending_uploads
                    .iter()
                    .enumerate()
                    .map(|(index, upload)| MessageAttachment {
                        index: u32::try_from(index).unwrap_or(u32::MAX),
                        name: upload.name.clone(),
                        media_type: upload.media_type.clone(),
                        mime_type: upload.mime_type.clone(),
                    })
                    .collect();
                app.add_message_with_attachments(
                    crate::util::short_id(),
                    MessageRole::User,
                    input.clone(),
                    attachments,
                );
                app.pending_responses = app.pending_responses.saturating_add(1);
                app.status_message = Some("PicoBot 正在处理…".to_string());
                let sent = send(
                    app,
                    WsInbound::UserInput {
                        content: input,
                        upload_ids,
                        client_message_id: None,
                        channel: None,
                        // Session routing is owned by the server. A full session
                        // id is not a chat id and must never be sent here.
                        chat_id: None,
                        sender_id: None,
                    },
                )
                .await;
                if !sent {
                    app.pending_responses = app.pending_responses.saturating_sub(1);
                } else {
                    app.pending_uploads.clear();
                }
            }
        }
        _ => {}
    }
}

async fn handle_modal_key(app: &mut App, key: KeyEvent) {
    match app.modal.take() {
        Some(Modal::Confirm(action)) => match key.code {
            KeyCode::Char('y') | KeyCode::Char('Y') | KeyCode::Enter => {
                let target = target_session_id(app);
                let message = match action {
                    ConfirmAction::Archive => target.map(|session_id| WsInbound::ArchiveSession {
                        session_id: Some(session_id),
                    }),
                    ConfirmAction::Delete => target.map(|session_id| WsInbound::DeleteSession {
                        session_id: Some(session_id),
                    }),
                    ConfirmAction::ClearHistory => {
                        target.map(|session_id| WsInbound::ClearHistory {
                            chat_id: None,
                            session_id: Some(session_id),
                        })
                    }
                };
                if let Some(message) = message {
                    send(app, message).await;
                    app.status_message = Some("正在更新会话…".to_string());
                }
            }
            KeyCode::Esc | KeyCode::Char('n') | KeyCode::Char('N') => {}
            _ => app.modal = Some(Modal::Confirm(action)),
        },
        Some(Modal::Rename {
            mut input,
            mut cursor,
        }) => match key.code {
            KeyCode::Esc => {}
            KeyCode::Enter => {
                let title = input.trim().to_string();
                if !title.is_empty() {
                    send(
                        app,
                        WsInbound::RenameSession {
                            session_id: target_session_id(app),
                            title,
                        },
                    )
                    .await;
                }
            }
            KeyCode::Char(character) => {
                input.insert(cursor, character);
                cursor += character.len_utf8();
                app.modal = Some(Modal::Rename { input, cursor });
            }
            KeyCode::Backspace => {
                if let Some((index, _)) = input[..cursor].char_indices().next_back() {
                    input.drain(index..cursor);
                    cursor = index;
                }
                app.modal = Some(Modal::Rename { input, cursor });
            }
            KeyCode::Delete => {
                if let Some(character) = input[cursor..].chars().next() {
                    input.drain(cursor..cursor + character.len_utf8());
                }
                app.modal = Some(Modal::Rename { input, cursor });
            }
            KeyCode::Left => {
                if let Some((index, _)) = input[..cursor].char_indices().next_back() {
                    cursor = index;
                }
                app.modal = Some(Modal::Rename { input, cursor });
            }
            KeyCode::Right => {
                if let Some(character) = input[cursor..].chars().next() {
                    cursor += character.len_utf8();
                }
                app.modal = Some(Modal::Rename { input, cursor });
            }
            _ => app.modal = Some(Modal::Rename { input, cursor }),
        },
        Some(Modal::AttachPath {
            mut input,
            mut cursor,
        }) => match key.code {
            KeyCode::Esc => {}
            KeyCode::Enter => {
                let path = input.trim().to_string();
                if !path.is_empty() {
                    upload_tui_file(app, &path).await;
                }
            }
            KeyCode::Char(character) => {
                input.insert(cursor, character);
                cursor += character.len_utf8();
                app.modal = Some(Modal::AttachPath { input, cursor });
            }
            KeyCode::Backspace => {
                if let Some((index, _)) = input[..cursor].char_indices().next_back() {
                    input.drain(index..cursor);
                    cursor = index;
                }
                app.modal = Some(Modal::AttachPath { input, cursor });
            }
            KeyCode::Delete => {
                if let Some(character) = input[cursor..].chars().next() {
                    input.drain(cursor..cursor + character.len_utf8());
                }
                app.modal = Some(Modal::AttachPath { input, cursor });
            }
            KeyCode::Left => {
                if let Some((index, _)) = input[..cursor].char_indices().next_back() {
                    cursor = index;
                }
                app.modal = Some(Modal::AttachPath { input, cursor });
            }
            KeyCode::Right => {
                if let Some(character) = input[cursor..].chars().next() {
                    cursor += character.len_utf8();
                }
                app.modal = Some(Modal::AttachPath { input, cursor });
            }
            _ => app.modal = Some(Modal::AttachPath { input, cursor }),
        },
        None => {}
    }
}

async fn upload_tui_file(app: &mut App, raw_path: &str) {
    let path = std::path::PathBuf::from(raw_path);
    let metadata = match tokio::fs::metadata(&path).await {
        Ok(metadata) if metadata.is_file() => metadata,
        _ => {
            app.status_message = Some("附件路径不是可读普通文件".to_string());
            return;
        }
    };
    let file_name = path
        .file_name()
        .and_then(|value| value.to_str())
        .unwrap_or("attachment")
        .to_string();
    let file = match tokio::fs::File::open(&path).await {
        Ok(file) => file,
        Err(error) => {
            app.status_message = Some(format!("无法打开附件：{error}"));
            return;
        }
    };
    app.status_message = Some(format!("正在上传 {file_name}…"));
    let body = reqwest::Body::wrap_stream(ReaderStream::new(file));
    let part = reqwest::multipart::Part::stream_with_length(body, metadata.len())
        .file_name(file_name.clone());
    let form = reqwest::multipart::Form::new().part("file", part);
    let url = format!("{}/api/chat/{}/uploads", app.http_base_url, app.client_id);
    let client = reqwest::Client::new();
    let mut request = client.post(url).multipart(form);
    if let Some(token) = &app.auth_token {
        request = request.bearer_auth(token);
    }
    match request.send().await {
        Ok(response) if response.status().is_success() => {
            match response.json::<UploadDescriptor>().await {
                Ok(upload) => {
                    app.status_message = Some(format!("已添加附件：{}", upload.name));
                    app.pending_uploads.push(upload);
                }
                Err(error) => app.status_message = Some(format!("上传响应无效：{error}")),
            }
        }
        Ok(response) => {
            let status = response.status();
            let detail = response.text().await.unwrap_or_default();
            app.status_message = Some(format!("上传失败 ({status})：{detail}"));
        }
        Err(error) => app.status_message = Some(format!("上传失败：{error}")),
    }
}

async fn download_latest_attachment(app: &mut App) {
    let Some(session_id) = app.current_session_id.clone() else {
        return;
    };
    let Some((message_id, attachment)) = app.messages.iter().rev().find_map(|message| {
        message
            .attachments
            .first()
            .map(|attachment| (message.id.clone(), attachment.clone()))
    }) else {
        app.status_message = Some("当前历史中没有附件".to_string());
        return;
    };
    let mut url = match reqwest::Url::parse(&app.http_base_url) {
        Ok(url) => url,
        Err(error) => {
            app.status_message = Some(format!("下载地址无效：{error}"));
            return;
        }
    };
    if let Ok(mut segments) = url.path_segments_mut() {
        segments.extend([
            "api",
            "chat",
            &app.client_id,
            "sessions",
            &session_id,
            "messages",
            &message_id,
            "attachments",
            &attachment.index.to_string(),
        ]);
    }
    let client = reqwest::Client::new();
    let mut request = client.get(url);
    if let Some(token) = &app.auth_token {
        request = request.bearer_auth(token);
    }
    let response = match request.send().await {
        Ok(response) if response.status().is_success() => response,
        Ok(response) => {
            app.status_message = Some(format!("附件不可用 ({})", response.status()));
            return;
        }
        Err(error) => {
            app.status_message = Some(format!("下载失败：{error}"));
            return;
        }
    };
    let target = unique_download_path(&attachment.name);
    let temporary = target.with_extension("picobot.part");
    let mut output = match tokio::fs::File::create(&temporary).await {
        Ok(output) => output,
        Err(error) => {
            app.status_message = Some(format!("无法创建下载文件：{error}"));
            return;
        }
    };
    let mut stream = response.bytes_stream();
    while let Some(chunk) = futures_util::StreamExt::next(&mut stream).await {
        match chunk {
            Ok(chunk) => {
                if let Err(error) = output.write_all(&chunk).await {
                    let _ = tokio::fs::remove_file(&temporary).await;
                    app.status_message = Some(format!("写入下载文件失败：{error}"));
                    return;
                }
            }
            Err(error) => {
                let _ = tokio::fs::remove_file(&temporary).await;
                app.status_message = Some(format!("下载中断：{error}"));
                return;
            }
        }
    }
    drop(output);
    if let Err(error) = tokio::fs::rename(&temporary, &target).await {
        let _ = tokio::fs::remove_file(&temporary).await;
        app.status_message = Some(format!("保存附件失败：{error}"));
        return;
    }
    app.status_message = Some(format!("附件已保存到 {}", target.display()));
}

fn unique_download_path(name: &str) -> std::path::PathBuf {
    let safe = std::path::Path::new(name)
        .file_name()
        .and_then(|value| value.to_str())
        .unwrap_or("attachment");
    let mut candidate = std::path::PathBuf::from(safe);
    let mut counter = 1_u32;
    while candidate.exists() {
        candidate = std::path::PathBuf::from(format!("{safe}.{counter}"));
        counter += 1;
    }
    candidate
}

fn open_rename(app: &mut App) {
    if target_session_id(app).is_some() {
        let input = if app.focus == Focus::Sessions {
            app.sessions
                .get(app.selected_session)
                .map(|session| session.title.clone())
                .unwrap_or_default()
        } else {
            app.current_title().to_string()
        };
        let cursor = input.len();
        app.modal = Some(Modal::Rename { input, cursor });
    }
}

fn target_session_id(app: &App) -> Option<String> {
    if app.focus == Focus::Sessions {
        app.selected_session_id()
    } else {
        app.current_session_id.clone()
    }
}

fn update_command_menu(app: &mut App) {
    app.show_command_menu = app.input.starts_with('/') && !app.input.contains('\n');
    app.selected_command_idx = app
        .selected_command_idx
        .min(app.get_filtered_commands().len().saturating_sub(1));
}

fn close_command_menu(app: &mut App) {
    app.show_command_menu = false;
    app.selected_command_idx = 0;
}

pub async fn request_session_list(app: &mut App) {
    send(
        app,
        WsInbound::ListSessions {
            include_archived: app.show_archived,
        },
    )
    .await;
}

pub async fn request_history(app: &mut App, session_id: String) {
    send(
        app,
        WsInbound::GetSessionHistory {
            session_id,
            limit: Some(1_000),
        },
    )
    .await;
}

pub async fn send(app: &mut App, inbound: WsInbound) -> bool {
    let serialized = match serialize_inbound(&inbound) {
        Ok(serialized) => serialized,
        Err(error) => {
            app.status_message = Some(format!("请求编码失败：{error}"));
            return false;
        }
    };
    let Some(sender) = &mut app.ws_sender else {
        app.connected = false;
        app.status_message = Some("Gateway 已断开".to_string());
        return false;
    };
    if let Err(error) = sender.send(Message::Text(serialized.into())).await {
        app.connected = false;
        app.status_message = Some(format!("发送失败：{error}"));
        return false;
    }
    true
}
