Skip to content
use actix_web::{HttpRequest, HttpResponse, Responder, get, web};
use maud::{Markup, html};

use crate::db::{Database, Notification};
use crate::session::SessionStore;
use crate::view::guard::guard_view;
use crate::view::{is_htmx, main_with_content};

fn debug_content(notifications: &[Notification]) -> Markup {
    html! {
        div class="flex min-h-screen w-full flex-col" {
            nav class="navbar bg-base-100 shadow-sm border-b border-base-200 px-4" {
                div class="navbar-start" {
                    a
                        href="/"
                        class="btn btn-ghost text-xl"
                        hx-get="/notify"
                        hx-target="#main"
                        hx-swap="innerHTML"
                        hx-push-url="/" {
                        "Notify"
                    }
                }
                div class="navbar-end gap-2" {
                    a
                        href="/install"
                        class="btn btn-ghost btn-sm"
                        hx-get="/install"
                        hx-target="#main"
                        hx-swap="innerHTML"
                        hx-push-url="true" {
                        "Install"
                    }
                    a
                        href="/"
                        class="btn btn-ghost btn-sm"
                        hx-get="/notify"
                        hx-target="#main"
                        hx-swap="innerHTML"
                        hx-push-url="/" {
                        "Back to notifications"
                    }
                }
            }

            div class="w-full flex-1 p-4 sm:p-8" {
                div class="mx-auto w-full max-w-6xl" {
                    div class="mb-6 flex flex-wrap items-center justify-between gap-3" {
                        div {
                            p class="text-sm font-semibold uppercase tracking-widest text-base-content/60" {
                                "Developer view"
                            }
                            h1 class="text-3xl font-bold" { "Notification database" }
                            p class="mt-1 text-base-content/70" {
                                (notifications.len()) " total entries"
                            }
                        }
                    }

                    @if notifications.is_empty() {
                        div class="card card-border bg-base-200" {
                            div class="card-body items-center text-center" {
                                h2 class="card-title" { "No notifications yet" }
                                p class="text-base-content/70" {
                                    "New notification entries will appear here."
                                }
                            }
                        }
                    } @else {
                        div class="overflow-x-auto rounded-box border border-base-300 bg-base-100" {
                            table class="table table-zebra table-sm" {
                                thead {
                                    tr {
                                        th { "ID" }
                                        th { "Kind" }
                                        th { "Message" }
                                        th { "Status" }
                                        th { "Created" }
                                    }
                                }
                                tbody {
                                    @for notification in notifications {
                                        tr {
                                            th class="font-mono" { (notification.id) }
                                            td class="font-medium" { (&notification.kind) }
                                            td class="min-w-64 whitespace-normal" { (&notification.message) }
                                            td {
                                                @if notification.sent {
                                                    span class="font-medium text-success" { "Sent" }
                                                } @else {
                                                    span class="font-medium text-warning" { "Pending" }
                                                }
                                            }
                                            td class="whitespace-nowrap text-base-content/70" {
                                                (&notification.created_at)
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

#[get("/debug")]
pub async fn debug(
    req: HttpRequest,
    session_store: web::Data<SessionStore>,
    db: web::Data<Database>,
) -> impl Responder {
    if !session_store.is_authenticated(&req) {
        let guard = guard_view(None);
        return if is_htmx(&req) {
            HttpResponse::Ok()
                .content_type("text/html; charset=utf-8")
                .body(guard.into_string())
        } else {
            HttpResponse::Ok()
                .content_type("text/html; charset=utf-8")
                .body(main_with_content(guard).into_string())
        };
    }

    match db.list_notifications().await {
        Ok(notifications) => {
            let content = debug_content(&notifications);
            if is_htmx(&req) {
                HttpResponse::Ok()
                    .content_type("text/html; charset=utf-8")
                    .body(content.into_string())
            } else {
                HttpResponse::Ok()
                    .content_type("text/html; charset=utf-8")
                    .body(main_with_content(content).into_string())
            }
        }
        Err(error) => {
            log::error!("Failed to load debug notifications: {error}");
            HttpResponse::InternalServerError().finish()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{App, cookie::Cookie, test as aw_test};

    #[actix_web::test]
    async fn test_debug_unauthenticated_returns_guard() {
        let session_store = web::Data::new(SessionStore::in_memory());
        let db = web::Data::new(Database::new(":memory:"));
        let app = aw_test::init_service(
            App::new()
                .app_data(session_store)
                .app_data(db)
                .service(debug),
        )
        .await;

        // Direct request wraps guard in full html
        let req = aw_test::TestRequest::get().uri("/debug").to_request();
        let resp = aw_test::call_service(&app, req).await;
        let body = aw_test::read_body(resp).await;
        let body_str = String::from_utf8(body.to_vec()).unwrap();

        assert!(body_str.contains("<!DOCTYPE html>"));
        assert!(body_str.contains("/assets/daisyui.css"));
        assert!(body_str.contains("Unlock"));

        // HTMX request returns partial guard
        let req_htmx = aw_test::TestRequest::get()
            .uri("/debug")
            .insert_header(("HX-Request", "true"))
            .to_request();
        let resp_htmx = aw_test::call_service(&app, req_htmx).await;
        let body_htmx = aw_test::read_body(resp_htmx).await;
        let body_htmx_str = String::from_utf8(body_htmx.to_vec()).unwrap();

        assert!(!body_htmx_str.contains("<!DOCTYPE html>"));
        assert!(body_htmx_str.contains("Unlock"));
    }

    #[actix_web::test]
    async fn test_debug_authenticated_lists_notifications() {
        let session_store = web::Data::new(SessionStore::in_memory());
        let token = session_store.create_session().await;
        let cookie = Cookie::build(crate::session::SESSION_COOKIE_NAME, token).finish();
        let db = Database::new(&format!(
            "/tmp/test_debug_view_{}.db",
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_nanos()
        ));
        db.init().await.unwrap();
        db.save_notification("heart", "Look at your phone", false)
            .await
            .unwrap();
        let db = web::Data::new(db);

        let app = aw_test::init_service(
            App::new()
                .app_data(session_store)
                .app_data(db)
                .service(debug),
        )
        .await;

        // Direct browser visit gets full layout with stylesheets
        let req = aw_test::TestRequest::get()
            .uri("/debug")
            .cookie(cookie.clone())
            .to_request();
        let resp = aw_test::call_service(&app, req).await;
        let body = aw_test::read_body(resp).await;
        let body = String::from_utf8(body.to_vec()).unwrap();

        assert!(body.contains("<!DOCTYPE html>"));
        assert!(body.contains("/assets/daisyui.css"));
        assert!(body.contains("/assets/app.css"));
        assert!(body.contains("Notification database"));
        assert!(body.contains("Look at your phone"));
        assert!(body.contains("Pending"));
        assert!(body.contains("class=\"navbar"));
        assert!(body.contains("href=\"/install\""));
        assert!(body.contains("Install"));
        assert!(body.contains("Back to notifications"));
        assert!(!body.contains("download=\"notify.json\""));
        assert!(!body.contains("Export config"));

        // HTMX request gets partial content for innerHTML swap
        let req_htmx = aw_test::TestRequest::get()
            .uri("/debug")
            .cookie(cookie)
            .insert_header(("HX-Request", "true"))
            .to_request();
        let resp_htmx = aw_test::call_service(&app, req_htmx).await;
        let body_htmx = aw_test::read_body(resp_htmx).await;
        let body_htmx = String::from_utf8(body_htmx.to_vec()).unwrap();

        assert!(!body_htmx.contains("<!DOCTYPE html>"));
        assert!(body_htmx.contains("Notification database"));
    }
}