Skip to content
//! HTTP handlers for the charts feature.

use actix_web::{HttpRequest, get, web};
use maud::{Markup, html};

use crate::{ConnectionStore, db};

use super::infer::infer_chart;
use super::query::{ChartQuery, DbQuery, fetch_chart_data};
use super::render::{error_svg, render_svg};
use super::types::ChartType;
use super::ui::{chart_area, chart_panel, no_connection_html, option_style, select_class};

/// Main charts page – renders the database selector.
#[get("/charts")]
pub async fn charts_page(
    req: HttpRequest,
    store: web::Data<ConnectionStore>,
) -> actix_web::Result<Markup> {
    let Some(ch) = store.active_client() else {
        return Ok(no_connection_html(&req));
    };

    let databases = db::all_databases(ch).await;

    let content = html! {
        div class="w-100 flex flex-column" style="height:100%;" {
            // ── Top selector bar ─────────────────────────────────────────
            div class="bg-black-70 pa3 bb b--white-10" style="flex-shrink:0;" {
                div class="flex items-end" style="gap:0.5rem;" {
                    div {
                        label class="db f7 white-50 mb1" { "Database" }
                        select
                            id="chart-db-select"
                            name="database"
                            class=(select_class())
                            style="color:white;"
                            hx-get="/charts/tables"
                            hx-include="[name='database']"
                            hx-target="#chart-table-container"
                            hx-swap="innerHTML"
                            hx-trigger="change" {
                            option value="" selected disabled style="background-color:#1a1a1a;color:#ccc;" {
                                "Database…"
                            }
                            @for db in &databases {
                                option value=(db.name) style=(option_style()) { (db.name) }
                            }
                        }
                    }
                    div id="chart-table-container" {}
                }
            }

            // ── Content area ─────────────────────────────────────────────
            div id="chart-content"
                class="w-100 flex-auto flex flex-column"
                style="overflow:hidden;" {
                div class="pa4 tc" {
                    p class="white-50 f5 i" { "Select a database and table to generate charts" }
                }
            }
        }
    };

    if req.headers().get("HX-Request").is_some() {
        Ok(content)
    } else {
        Ok(crate::view::render_layout(&content))
    }
}

/// Returns the table `<select>` for the chosen database (HTMX partial).
#[get("/charts/tables")]
pub async fn charts_get_tables(
    query: web::Query<DbQuery>,
    store: web::Data<ConnectionStore>,
) -> actix_web::Result<Markup> {
    let db_name = match query.database.as_deref() {
        Some(s) if !s.is_empty() => s,
        _ => return Ok(html! {}),
    };

    let Some(ch) = store.active_client() else {
        return Ok(html! { p class="white-70 f6" { "No active connection." } });
    };

    let tables = db::all_tables(ch, db_name).await;

    Ok(html! {
        div {
            label class="db f7 white-50 mb1" { "Table" }
            select
                id="chart-table-select"
                name="table"
                class=(select_class())
                style="color:white;"
                hx-get="/charts/view"
                hx-include="[name='database'],[name='table']"
                hx-target="#chart-content"
                hx-swap="innerHTML"
                hx-trigger="change" {
                option value="" selected disabled style="background-color:#1a1a1a;color:#ccc;" {
                    "Table…"
                }
                @for t in &tables {
                    option value=(t.name) style=(option_style()) { (t.name) }
                }
            }
        }
    })
}

/// Renders the full chart view (controls + initial chart) for the chosen table.
#[get("/charts/view")]
pub async fn charts_view(
    query: web::Query<ChartQuery>,
    store: web::Data<ConnectionStore>,
) -> actix_web::Result<Markup> {
    let Some(conn) = store.active() else {
        return Ok(html! { p class="white-70 f6 pa3" { "No active connection." } });
    };

    let cols = db::describe_table(&conn, &query.database, &query.table).await;
    if cols.is_empty() {
        return Ok(html! {
            p class="white-70 f6 pa3" {
                "Could not describe table \"" (query.table) "\". Check permissions."
            }
        });
    }

    let (inferred_type, inferred_x, inferred_y) = infer_chart(&cols);
    let chart_type = query.chart_type.clone().unwrap_or(inferred_type);
    let x_col = query
        .x_col
        .clone()
        .filter(|s| !s.is_empty())
        .unwrap_or(inferred_x);
    let y_col = query
        .y_col
        .clone()
        .filter(|s| !s.is_empty())
        .unwrap_or(inferred_y);
    let limit = query.limit;

    let (svg, data_len, err_msg) = match fetch_chart_data(
        &conn,
        &query.database,
        &query.table,
        &chart_type,
        &x_col,
        &y_col,
        limit,
    )
    .await
    {
        Ok(data) => {
            let len = data.len();
            let svg = render_svg(&chart_type, &data, &x_col, &y_col);
            (svg, len, None)
        }
        Err(e) => {
            log::error!("Chart data fetch error: {e}");
            let svg = error_svg(&format!("Query error: {e}"));
            (svg, 0, Some(e))
        }
    };

    Ok(chart_panel(
        &cols,
        &chart_type,
        &x_col,
        &y_col,
        limit,
        &query.database,
        &query.table,
        &svg,
        data_len,
        err_msg.as_deref(),
    ))
}

/// Re-renders just the `#chart-area` when the user adjusts controls.
#[get("/charts/render")]
pub async fn charts_render(
    query: web::Query<ChartQuery>,
    store: web::Data<ConnectionStore>,
) -> actix_web::Result<Markup> {
    let Some(conn) = store.active() else {
        return Ok(html! {
            div id="chart-area" { p class="white-70 f6 pa3" { "No active connection." } }
        });
    };

    let chart_type = query.chart_type.clone().unwrap_or(ChartType::Bar);
    let x_col = query.x_col.clone().unwrap_or_default();
    let y_col = query.y_col.clone().unwrap_or_default();
    let limit = query.limit;

    if x_col.is_empty() {
        return Ok(html! {
            div id="chart-area" { p class="white-70 f6 pa3" { "Select an X column." } }
        });
    }

    let (svg, data_len, err_msg) = match fetch_chart_data(
        &conn,
        &query.database,
        &query.table,
        &chart_type,
        &x_col,
        &y_col,
        limit,
    )
    .await
    {
        Ok(data) => {
            let len = data.len();
            let svg = render_svg(&chart_type, &data, &x_col, &y_col);
            (svg, len, None)
        }
        Err(e) => {
            log::error!("Chart render error: {e}");
            let svg = error_svg(&format!("Query error: {e}"));
            (svg, 0, Some(e))
        }
    };

    Ok(chart_area(&svg, data_len, err_msg.as_deref()))
}