use std::collections::{HashMap, HashSet};

use axum::{
    Json,
    extract::{FromRequest, FromRequestParts, Path, State},
    http::StatusCode,
    response::{IntoResponse, sse::KeepAlive},
};
//use axum::extract::ws::Message;
use rusqlite::OptionalExtension;
use rusqlite::{Name, Statement, params};
use serde::Serialize;
use serde_json::{json, to_value};

use crate::chat::extractor::{
    AddUserConversationPayload, CreateConversationPayload, GetMessagesPayload, SendMessagePayload,
};
use crate::utils::auth::AuthClaims;
use crate::utils::db_pool::AppState;

//TODO: update conversation title
//FIXME: make a default title if empty
pub async fn create_conversation(
    State(state): State<AppState>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    CreateConversationPayload(payload): CreateConversationPayload,
) -> impl IntoResponse {
    let pool = state.hotel_pools.get_pool(hotel_id);

    let conn = match pool.get() {
        Ok(conn) => conn,
        Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error")),
    };

    let result = conn.execute(
        "INSERT INTO conversation (creator_id, name) VALUES (?1, ?2)",
        params![&user_id, &payload.name],
    );

    let rows = match result {
        Ok(rows) => rows,
        Err(err) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Error when creating the conversation: {err}"),
            );
        }
    };

    if rows == 0 {
        return (
            StatusCode::NOT_FOUND,
            "not able to create the conversation".to_string(),
        );
    }

    let conv_id = conn.last_insert_rowid();

    let user_conv_rel = conn.execute(
        "INSERT INTO conversation_participants (conversation_id, user_id, name)
             VALUES (?1, ?2, ?3)",
        params![conv_id, user_id, payload.name],
    );

    match user_conv_rel {
        Ok(r) => {
            return (
                StatusCode::OK,
                format!("Created conversation {}", payload.name),
            );
        }
        Err(err) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Error when creating the conversation: {err}"),
            );
        }
    }

    // extra logic here (more queries, validations, etc.)

    //(StatusCode::OK, format!("Created conversation {}", payload.name))
}

//FIXME: add title to conv
pub async fn add_user_to_conv(
    State(state): State<AppState>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    AddUserConversationPayload(payload): AddUserConversationPayload,
) -> impl IntoResponse {
    let pool = state.hotel_pools.get_pool(hotel_id);

    let mut conn = match pool.get() {
        Ok(conn) => conn,
        Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error")),
    };

    let creator_name: Option<String> = match conn
        .query_row(
            "SELECT name FROM conversation WHERE creator_id = ?1 AND id = ?2",
            params![user_id, payload.conv_id],
            |row| row.get(0),
        )
        .optional()
    {
        Ok(name) => name,
        //Ok(None) => false,
        Err(_) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Creator check failed".to_string(),
            );
        }
    };

    let is_creator = creator_name.is_some();

    if !is_creator {
        return (
            StatusCode::FORBIDDEN,
            "Not the creator of the conversation".to_string(),
        );
    }

    //fix this
    let existing: HashSet<u32> = {
        let mut stmt = match conn
            .prepare("SELECT user_id FROM conversation_participants WHERE conversation_id = ?1")
        {
            Ok(s) => s,
            Err(_) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Prepare participants stmt failed".to_string(),
                );
            }
        };

        match stmt.query_map(params![payload.conv_id], |row| row.get(0)) {
            Ok(rows) => rows.filter_map(Result::ok).collect(),
            Err(_) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Query participants failed".to_string(),
                );
            }
        }
    }; // ← stmt dropped HERE

    let payload_users: HashSet<u32> = payload.users.into_iter().collect();

    let to_add: Vec<u32> = payload_users.difference(&existing).copied().collect();

    let to_remove: Vec<u32> = existing.difference(&payload_users).copied().collect();

    let tx = match conn.transaction() {
        Ok(t) => t,
        Err(_) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Transaction start failed for update conv participants".to_string(),
            );
        }
    };

    for user_id in to_add {
        if let Err(err) = tx.execute(
            "INSERT INTO conversation_participants (conversation_id, user_id, name)
             VALUES (?1, ?2, ?3)",
            params![payload.conv_id, user_id, creator_name],
        ) {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Insert failed for {}: {}", user_id, err),
            );
        }
    }

    for user_id in to_remove {
        if let Err(err) = tx.execute(
            "DELETE FROM conversation_participants
             WHERE conversation_id = ?1 AND user_id = ?2",
            params![payload.conv_id, user_id],
        ) {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Delete failed for {}: {}", user_id, err),
            );
        }
    }

    if let Err(_) = tx.commit() {
        return (
            StatusCode::INTERNAL_SERVER_ERROR,
            "Transaction commit failed".to_string(),
        );
    }

    return (StatusCode::OK, "ok".to_string());
}

/*
#[derive(Deserialize, Serialize, Debug)]
pub struct Get

*/

pub async fn get_conv_users(
    State(state): State<AppState>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    Path(conv_id): Path<i32>,
) -> impl IntoResponse {
    let pool = state.hotel_pools.get_pool(hotel_id);

    let conn = match pool.get() {
        Ok(c) => c,
        Err(err) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Error opening pol connection : {err}"),
            );
        }
    };

    let mut stmt = match conn
        .prepare("SELECT conversation_id, name FROM conversation_participants WHERE user_id = ?1")
    {
        Ok(s) => s,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Prepare failed: {}", e),
            );
        }
    };

    //fix this
    let existing: HashSet<u32> = {
        let mut stmt = match conn
            .prepare("SELECT user_id FROM conversation_participants WHERE conversation_id = ?1")
        {
            Ok(s) => s,
            Err(_) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Prepare participants stmt failed".to_string(),
                );
            }
        };

        match stmt.query_map(params![conv_id], |row| row.get(0)) {
            Ok(rows) => rows.filter_map(Result::ok).collect(),
            Err(_) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Query participants failed".to_string(),
                );
            }
        }
    }; // ← stmt dropped HERE

    let present: Vec<u32> = existing.into_iter().collect();

    match serde_json::to_string(&present) {
        Ok(json) => (StatusCode::OK, json),
        Err(e) => (
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Serialization failed: {}", e),
        ),
    }
}

pub async fn send_message(
    State(state): State<AppState>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    SendMessagePayload(payload): SendMessagePayload,
) -> impl IntoResponse {
    //TODO: make sur the convid is valid
    let pool = state.hotel_pools.get_pool(hotel_id);

    let conn = match pool.get() {
        Ok(conn) => conn,
        Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error")),
    };

    let mut statement = match conn.prepare(
        "SELECT 1 FROM conversation_participants WHERE user_id = ?1 AND conversation_id = ?2",
    ) {
        Ok(statement) => statement,
        Err(_) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                "prepare failed".to_string(),
            );
        }
    };

    match statement.exists(params![user_id, payload.conv_id]) {
        Ok(true) => {
        }
        Ok(false) => {
            return (
                StatusCode::FORBIDDEN,
                "Not part of the conversation".to_string(),
            );
        }
        Err(_) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Query failed".to_string(),
            );
        }
    }

    let (message_id, sent_at): (i64, String) = match conn.query_row(
        "INSERT INTO message (sender_id, content, conversation_id)
     VALUES (?1, ?2, ?3)
     RETURNING id, sent_at",
        params![user_id, payload.message, payload.conv_id],
        |row| Ok((row.get::<_, i64>(0)?, row.get::<_, String>(1)?)),
    ) {
        Ok(v) => v,
        Err(err) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("DB insert failed: {err}"),
            );
        }
    };
    let mut stmt_participants = conn
        .prepare("SELECT user_id FROM conversation_participants WHERE conversation_id = ?1")
        .expect("prepare participants failed");

    let participant_ids: Vec<i32> = stmt_participants
        .query_map(params![payload.conv_id], |row| row.get(0))
        .expect("query_map failed")
        .filter_map(Result::ok)
        .collect();

    if let Some(hotel_users) = state.ws_map.get(&hotel_id) {
        let update_msg = serde_json::json!({
            "event_type": "chat_message",
            "conv_id": payload.conv_id,
            "sender_id": user_id,
            "content": payload.message,
            "id": message_id,
            "sent_at": sent_at,
        })
        .to_string();

        for uid in &participant_ids {
            if let Some(sender) = hotel_users.get(uid) {
                let _ = sender.send(axum::extract::ws::Message::Text(update_msg.clone().into()));
            }
        }
    }

    (
        StatusCode::OK,
        format!(
            "sent message: {}, to users:{:?}",
            payload.message, participant_ids
        ),
    )
}

#[derive(Debug, Serialize)]
struct Message {
    id: i32,
    sender_id: i32,
    content: String,
    sent_at: String,
}

pub async fn get_message(
    State(state): State<AppState>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    GetMessagesPayload(payload): GetMessagesPayload,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let pool = state.hotel_pools.get_pool(hotel_id);

    let conn = pool
        .get()
        .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Pool error".to_string()))?;

    let from_time = match payload.timestamp.as_deref() {
        Some("0") | None => "1970-01-01 00:00:00", // default to epoch
        Some(ts) => ts,
    };

    let mut stmt = conn
        .prepare(
            "SELECT id, sender_id, content, sent_at
         FROM message
         WHERE conversation_id = ?1
           AND sent_at > ?2
         ORDER BY sent_at DESC
         LIMIT 50",
        )
        .map_err(|_| {
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Prepare failed".to_string(),
            )
        })?;

    let messages = stmt
        .query_map(params![payload.conv_id, from_time], |row| {
            Ok(Message {
                id: row.get(0)?,
                sender_id: row.get(1)?,
                content: row.get(2)?,
                sent_at: row.get(3)?,
            })
        })
        .map_err(|_| {
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Query failed".to_string(),
            )
        })?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| {
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "Collect failed".to_string(),
            )
        })?;

    let response = serde_json::to_string(&messages)
        .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Serialisation failed"));

    Ok((StatusCode::OK, response))
}

#[derive(Serialize)]
struct User {
    id: i32,
    username: String,
    //display_name: String,
}

pub async fn get_hotel_users(
    State(state): State<AppState>,
    AuthClaims { hotel_id, .. }: AuthClaims,
) -> impl IntoResponse {
    let conn = match state.logs_pool.get() {
        Ok(c) => c,
        Err(_) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                "DB connection error".to_string(),
            );
        }
    };

    let mut stmt =
        match conn.prepare("SELECT user_id, username FROM hotel_user_link WHERE hotel_id = ?1") {
            Ok(s) => s,
            Err(e) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    format!("Prepare failed: {}", e),
                );
            }
        };

    let users_iter = match stmt.query_map(params![hotel_id], |row| {
        Ok(User {
            id: row.get(0)?,
            username: row.get(1)?,
            //display_name: row.get(2)?,
        })
    }) {
        Ok(iter) => iter,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Query failed: {}", e),
            );
        }
    };

    let users: Vec<User> = match users_iter.collect::<Result<Vec<_>, _>>() {
        Ok(u) => u,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Collect failed: {}", e),
            );
        }
    };

    match serde_json::to_string(&users) {
        Ok(json) => (StatusCode::OK, json),
        Err(e) => (
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Serialization failed: {}", e),
        ),
    }
}

#[derive(Debug, Serialize)]
struct Conversation {
    id: i32,
    title: String,
}

pub async fn get_convs(
    State(state): State<AppState>,
    //Path((item_name, item_amount)): Path<(String, i32)>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
) -> impl IntoResponse {
    let pool = state.hotel_pools.get_pool(hotel_id);

    let conn = match pool.get() {
        Ok(conn) => conn,
        Err(err) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Pool error: {err}"),
            );
        }
    };

    let mut stmt = match conn
        .prepare("SELECT conversation_id, name FROM conversation_participants WHERE user_id = ?1")
    {
        Ok(s) => s,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Prepare failed: {}", e),
            );
        }
    };

    let rows = match stmt.query_map(params![user_id], |row| {
        let conversation_id: i32 = row.get(0)?;
        let name: String = row.get(1)?;
        Ok(Conversation {
            id: row.get(0)?,
            title: row.get(1)?,
        })
    }) {
        Ok(rows) => rows,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Query failed: {}", e),
            );
        }
    };

    let convs: Vec<Conversation> = match rows.collect::<Result<Vec<_>, _>>() {
        Ok(u) => u,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Collect failed: {}", e),
            );
        }
    };
    match serde_json::to_string(&convs) {
        Ok(json) => (StatusCode::OK, json),
        Err(e) => (
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("Serialization failed: {}", e),
        ),
    }
}
