use axum::{Json, extract::Path, extract::State   };
use axum::response::IntoResponse;
use axum::http::StatusCode;
use axum::extract::ws::Message;
use serde::Serialize;
use serde_json::json;

use crate::rooms::extractor::UpdateRoomPayload;
//use crate::utils::db_pool::*;
use crate::utils::auth::AuthClaims;
use crate::utils::db_pool::{HotelPool,AppState};

use std::sync::Arc;
use r2d2::{Pool};
use r2d2_sqlite::SqliteConnectionManager;
use dashmap::DashMap;
use rusqlite::params;



pub async fn hello_rooms() -> String {
    "hello from rooms".to_string()
}

pub async fn fake_db_update(
    State(state): State<AppState>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    Path(room_id): Path<i32>,
    UpdateRoomPayload(payload): UpdateRoomPayload,
) -> 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 result = conn.execute(
        "UPDATE rooms SET status = ?1 WHERE number = ?2",
        params![&payload.status, &room_id],
    );

    match result {
        Ok(rows) if rows > 0 => (StatusCode::OK, format!("Updated room {room_id} in hotel {}", hotel_id)),
        Ok(_) => (StatusCode::NOT_FOUND, "No room found".to_string()),
        Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {err}")),
    }
}

pub async fn clean_db_update(
    State(state): State<AppState>,
    Path(room_id): Path<i32>,
    AuthClaims { user_id, hotel_id }: AuthClaims,
    UpdateRoomPayload(payload): UpdateRoomPayload,
) -> impl IntoResponse {

    //TODO: make better error handling :
        // if wrong param collumn targeted,
        // if missing path param

    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 result: Result<String, rusqlite::Error> = conn.query_row(
        "UPDATE rooms SET status = ?1 WHERE id = ?2 RETURNING  number",
        params![&payload.status, &room_id],
        |row| row.get(0),
    );

    
    match result {
        Ok(room_number) => {
            // --- broadcast to all WS clients in the hotel ---
            if let Err(err) = conn.execute(
                "INSERT INTO room_history (room_id, room_number, status) VALUES (?1, ?2, ?3)",
                params![&room_id, &room_number, &payload.status],
            ) {
                return (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to insert history: {err}"));
            }
            if let Some(hotel_users) = state.ws_map.get(&hotel_id) {
                let update_msg = json!({
                    "event-type": "room-update",
                    "room_id": room_id,
                    "status": payload.status,
                    "updated_by": user_id,
                })
                .to_string();

                for entry in hotel_users.iter() {
                    let sender = entry.value();
                    // ignore errors (client disconnected)
                    let _ = sender.send(Message::Text(update_msg.clone().into()));
                }
            }

            (StatusCode::OK, format!("updated room {room_id} in hotel {hotel_id}, with status: {}", payload.status))
        }
        Ok(_) => (StatusCode::NOT_FOUND, "No room found".to_string()),
        Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Error from DB: {err}")),
    }
}

#[derive(Serialize)]
struct Room {
    id: i32,
    number: String,
    status: String,
}

pub async fn get_all_rooms(
    State(state): State<AppState>,
    AuthClaims { 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 id, number, status FROM rooms ",
    ) {
        Ok(s) => s,
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e)),
    };

    let room_iter = match stmt.query_map( 
        params![],|row| {
            Ok(Room {
                id: row.get(0)?,
                number: row.get(1)?,
                status: row.get(2)?,
            })
        }
    ) {
        Ok(iter) => iter,
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Query failed: {}", e)),
    };

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

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



struct RoomRequest {
    item_id: i32,
    item_amount: i32,
    token: String,
}

