Skip to content
use actix_multipart::Multipart;
use actix_web::Result as AwResult;
use actix_web::{HttpResponse, post, web};
use futures_util::StreamExt;
use log::{error, info};
use maud::html;

use crate::{ConnectionStore, db};

#[post("/upload/csv")]
pub async fn upload_csv(
    mut payload: Multipart,
    store: web::Data<ConnectionStore>,
) -> AwResult<HttpResponse> {
    let Some(ch) = store.active_client() else {
        let markup = html! {
            div class="bg-dark-red white pa3 br2" {
                p class="f5 fw6 ma0 mb2" { "No active connection" }
                p class="f6 ma0" {
                    "Please configure a database connection in "
                    a href="/connections" class="white underline" { "Connections" }
                    " first."
                }
            }
        };
        return Ok(HttpResponse::ServiceUnavailable()
            .content_type("text/html")
            .body(markup.into_string()));
    };

    let mut file_name: Option<String> = None;
    let mut file_data: Vec<u8> = Vec::new();

    while let Some(field) = payload.next().await {
        let mut field = match field {
            Ok(f) => f,
            Err(e) => {
                error!("Error reading multipart field: {}", e);
                let markup = html! {
                    div class="bg-dark-red white pa3 br2" {
                        p class="f5 fw6 ma0 mb2" { "Upload Error" }
                        p class="f6 ma0" { "Error reading file: " (e) }
                    }
                };
                return Ok(HttpResponse::BadRequest()
                    .content_type("text/html")
                    .body(markup.into_string()));
            }
        };

        let content_disposition = field.content_disposition();
        let field_name = content_disposition
            .and_then(|cd| cd.get_name())
            .unwrap_or("");

        if field_name == "file" {
            if let Some(fname) = content_disposition.and_then(|cd| cd.get_filename()) {
                file_name = Some(
                    fname
                        .trim_end_matches(".csv")
                        .replace(['-', ' ', '.'], "_")
                        .to_string(),
                );
            }

            while let Some(chunk) = field.next().await {
                let chunk = match chunk {
                    Ok(c) => c,
                    Err(e) => {
                        error!("Error reading file chunk: {}", e);
                        let markup = html! {
                            div class="bg-dark-red white pa3 br2" {
                                p class="f5 fw6 ma0 mb2" { "Upload Error" }
                                p class="f6 ma0" { "Error reading file data: " (e) }
                            }
                        };
                        return Ok(HttpResponse::BadRequest()
                            .content_type("text/html")
                            .body(markup.into_string()));
                    }
                };
                file_data.extend_from_slice(&chunk);
            }
        }
    }

    if file_data.is_empty() {
        let markup = html! {
            div class="bg-orange white pa3 br2" {
                p class="f5 fw6 ma0 mb2" { "No File" }
                p class="f6 ma0" { "Please select a CSV file to upload." }
            }
        };
        return Ok(HttpResponse::BadRequest()
            .content_type("text/html")
            .body(markup.into_string()));
    }

    let table_name = file_name.unwrap_or_else(|| "uploaded_table".to_string());

    info!(
        "Processing CSV upload: {} ({} bytes)",
        table_name,
        file_data.len()
    );

    match db::create_table_from_csv(ch, &table_name, &file_data).await {
        Ok(_) => {
            info!("Successfully created table: {}", table_name);
            let markup = html! {
                div class="bg-dark-green white pa3 br2" {
                    p class="f5 fw6 ma0 mb2" { "Success!" }
                    p class="f6 ma0 mb2" {
                        "Table '" (table_name) "' has been created successfully."
                    }
                    p class="f6 ma0" {
                        "You can now query it using SQL."
                    }
                }
            };
            Ok(HttpResponse::Ok()
                .content_type("text/html")
                .body(markup.into_string()))
        }
        Err(e) => {
            error!("Failed to create table from CSV: {}", e);
            let markup = html! {
                div class="bg-dark-red white pa3 br2" {
                    p class="f5 fw6 ma0 mb2" { "Database Error" }
                    p class="f6 ma0" { "Failed to create table: " (e) }
                }
            };
            Ok(HttpResponse::InternalServerError()
                .content_type("text/html")
                .body(markup.into_string()))
        }
    }
}