use std::error::Error as StdError;
use std::path::PathBuf;

use myfsio_server::templates::TemplateEngine;
use serde_json::{json, Value};
use tera::Context;

fn engine() -> TemplateEngine {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("templates");
    path.push("*.html");
    let glob = path.to_string_lossy().replace('\\', "/");
    let engine = TemplateEngine::new(&glob).expect("template parse");
    myfsio_server::handlers::ui_pages::register_ui_endpoints(&engine);
    engine
}

fn base_ctx() -> Context {
    let mut ctx = Context::new();
    ctx.insert("csrf_token_value", &"test-csrf");
    ctx.insert("is_authenticated", &true);
    ctx.insert("current_user", &"test");
    ctx.insert("current_user_display_name", &"Test User");
    ctx.insert("current_endpoint", &"");
    ctx.insert("request_args", &serde_json::json!({}));
    ctx.insert(
        "principal",
        &json!({
            "access_key": "AKIATESTTEST",
            "user_id": "u-test",
            "display_name": "Test Admin",
            "is_admin": true
        }),
    );
    ctx.insert("can_manage_iam", &true);
    ctx.insert("can_manage_replication", &true);
    ctx.insert("can_manage_sites", &true);
    ctx.insert("can_manage_encryption", &false);
    ctx.insert("website_hosting_nav", &false);
    ctx.insert("encryption_enabled", &false);
    ctx.insert("kms_enabled", &false);
    ctx.insert("flashed_messages", &Vec::<Value>::new());
    ctx.insert("null", &Value::Null);
    ctx.insert("none", &Value::Null);
    ctx
}

fn format_err(e: tera::Error) -> String {
    let mut out = format!("{}", e);
    let mut src = StdError::source(&e);
    while let Some(s) = src {
        out.push_str("\n  caused by: ");
        out.push_str(&s.to_string());
        src = s.source();
    }
    out
}

fn render_or_panic(tmpl: &str, ctx: &Context) {
    let e = engine();
    match e.render(tmpl, ctx) {
        Ok(_) => {}
        Err(err) => panic!("{} failed:\n{}", tmpl, format_err(err)),
    }
}

fn render_to_string_or_panic(tmpl: &str, ctx: &Context) -> String {
    let e = engine();
    match e.render(tmpl, ctx) {
        Ok(rendered) => rendered,
        Err(err) => panic!("{} failed:\n{}", tmpl, format_err(err)),
    }
}

#[test]
fn render_buckets() {
    let mut ctx = base_ctx();
    ctx.insert(
        "buckets",
        &json!([{
            "meta": {"name": "b1", "creation_date": "2024-01-01T00:00:00Z"},
            "summary": {"human_size": "0 B", "objects": 0},
            "detail_url": "/ui/buckets/b1",
            "access_badge": "bg-secondary",
            "access_label": "Private"
        }]),
    );
    render_or_panic("buckets.html", &ctx);
}

#[test]
fn render_connections() {
    let mut ctx = base_ctx();
    ctx.insert(
        "connections",
        &json!([{
            "id": "c1",
            "name": "Prod",
            "endpoint_url": "https://s3.example.com",
            "region": "us-east-1",
            "access_key": "AKIAEXAMPLEKEY12345"
        }]),
    );
    render_or_panic("connections.html", &ctx);
}

#[test]
fn render_iam() {
    let mut ctx = base_ctx();
    ctx.insert(
        "users",
        &json!([{
            "user_id": "u-1",
            "access_key": "AKIA1",
            "display_name": "Alice",
            "enabled": true,
            "is_enabled": true,
            "expires_at": "2026-04-21T00:00:00Z",
            "is_admin": true,
            "is_expired": false,
            "is_expiring_soon": true,
            "access_keys": [{"access_key": "AKIA1", "status": "active", "created_at": "2024-01-01"}],
            "policy_count": 1,
            "policies": [{"bucket": "*", "actions": ["*"], "prefix": "*"}]
        }]),
    );
    ctx.insert("iam_locked", &false);
    ctx.insert("locked_reason", &"");
    ctx.insert("iam_disabled", &false);
    ctx.insert("all_buckets", &Vec::<String>::new());
    ctx.insert("disclosed_secret", &Value::Null);
    ctx.insert("config_document", &"");
    ctx.insert("config_summary", &json!({"user_count": 1}));
    let rendered = render_to_string_or_panic("iam.html", &ctx);
    assert!(rendered.contains("data-user-id=\"u-1\""));
    assert!(rendered.contains("data-access-key=\"AKIA1\""));
    assert!(rendered.contains("Expiring soon"));
}

#[test]
fn render_metrics() {
    let mut ctx = base_ctx();
    ctx.insert("metrics_enabled", &false);
    ctx.insert("metrics_history_enabled", &false);
    ctx.insert("operation_metrics_enabled", &false);
    ctx.insert("history", &Vec::<Value>::new());
    ctx.insert("operation_metrics", &Vec::<Value>::new());
    ctx.insert("cpu_percent", &0);
    ctx.insert(
        "memory",
        &json!({ "percent": 0, "total": "0 B", "used": "0 B" }),
    );
    ctx.insert(
        "disk",
        &json!({ "percent": 0, "free": "0 B", "total": "0 B" }),
    );
    ctx.insert(
        "app",
        &json!({
            "buckets": 0, "objects": 0, "storage_used": "0 B",
            "uptime_days": 0, "versions": 0
        }),
    );
    ctx.insert("has_issues", &false);
    ctx.insert(
        "summary",
        &json!({
            "app": {"buckets": 0, "objects": 0, "storage_used": "0 B", "uptime_days": 0, "versions": 0},
            "cpu_percent": 0,
            "disk": {"free": 0, "percent": 0, "total": 0},
            "memory": {"percent": 0, "total": 0, "used": 0},
            "has_issues": false
        }),
    );
    render_or_panic("metrics.html", &ctx);
}

#[test]
fn render_system() {
    let mut ctx = base_ctx();
    ctx.insert("app_version", &"0.1.0");
    ctx.insert("display_timezone", &"UTC");
    ctx.insert("platform", &"linux");
    ctx.insert("python_version", &"n/a");
    ctx.insert("storage_root", &"/tmp/data");
    ctx.insert("has_rust", &true);
    ctx.insert("total_issues", &0);
    ctx.insert("features", &Vec::<Value>::new());
    ctx.insert("gc_history", &Vec::<Value>::new());
    ctx.insert("integrity_history", &Vec::<Value>::new());
    ctx.insert(
        "gc_status",
        &json!({
            "dry_run": false, "enabled": false, "interval_hours": 6,
            "lock_file_max_age_hours": 1, "multipart_max_age_days": 7,
            "scanning": false, "temp_file_max_age_hours": 24
        }),
    );
    ctx.insert(
        "integrity_status",
        &json!({
            "auto_heal": false, "batch_size": 100, "dry_run": false,
            "enabled": false, "interval_hours": 24, "scanning": false
        }),
    );
    render_or_panic("system.html", &ctx);
}

#[test]
fn render_sites() {
    let mut ctx = base_ctx();
    ctx.insert("local_site", &Value::Null);
    ctx.insert("peers", &Vec::<Value>::new());
    ctx.insert("peers_with_stats", &Vec::<Value>::new());
    ctx.insert("connections", &Vec::<Value>::new());
    ctx.insert("config_site_id", &"");
    ctx.insert("config_site_endpoint", &"");
    ctx.insert("config_site_region", &"us-east-1");
    render_or_panic("sites.html", &ctx);
}

#[test]
fn render_cluster_empty() {
    let mut ctx = base_ctx();
    ctx.insert("cluster_sites", &Vec::<Value>::new());
    ctx.insert("cluster_total_buckets", &0u64);
    ctx.insert("cluster_total_objects", &0u64);
    ctx.insert("cluster_total_size_bytes", &0u64);
    ctx.insert("cluster_online_count", &0usize);
    ctx.insert("cluster_total_count", &0usize);
    render_or_panic("cluster.html", &ctx);
}

#[test]
fn render_cluster_with_sites() {
    let mut ctx = base_ctx();
    let sites = json!([
        {
            "site_id": "local-1",
            "display_name": "Local",
            "endpoint": "http://127.0.0.1:8000",
            "region": "us-east-1",
            "online": true,
            "stale": false,
            "is_local": true,
            "buckets": 3,
            "objects": 42,
            "size_bytes": 1048576,
            "capacity": {"total_bytes": 100000000, "available_bytes": 50000000},
            "system": {"cpu_percent": 12.5, "memory_percent": 33.0, "disk_percent": 50.0, "storage_bytes": 1048576},
            "sync": {"errors": 0, "last_sync_at": 1700000000.0},
            "error": null
        },
        {
            "site_id": "peer-1",
            "display_name": "Peer",
            "endpoint": "http://peer.example.com",
            "online": false,
            "stale": true,
            "is_local": false,
            "registered_region": "us-west-2",
            "registered_priority": 100,
            "error": "request failed: timeout"
        }
    ]);
    ctx.insert("cluster_sites", &sites);
    ctx.insert("cluster_total_buckets", &3u64);
    ctx.insert("cluster_total_objects", &42u64);
    ctx.insert("cluster_total_size_bytes", &1048576u64);
    ctx.insert("cluster_online_count", &1usize);
    ctx.insert("cluster_total_count", &2usize);
    render_or_panic("cluster.html", &ctx);
}

#[test]
fn render_website_domains() {
    let mut ctx = base_ctx();
    ctx.insert("mappings", &Vec::<Value>::new());
    ctx.insert("buckets", &Vec::<String>::new());
    render_or_panic("website_domains.html", &ctx);
}

#[test]
fn render_replication_wizard() {
    let mut ctx = base_ctx();
    ctx.insert("connections", &Vec::<Value>::new());
    ctx.insert("local_site", &Value::Null);
    ctx.insert("peers", &Vec::<Value>::new());
    ctx.insert("buckets", &Vec::<Value>::new());
    ctx.insert(
        "peer",
        &json!({
            "site_id": "peer-1",
            "display_name": "Peer One",
            "endpoint": "https://peer.example.com",
            "region": "us-east-1"
        }),
    );
    ctx.insert(
        "connection",
        &json!({
            "id": "c1", "name": "Prod",
            "endpoint_url": "https://s3.example.com",
            "region": "us-east-1",
            "access_key": "AKIA"
        }),
    );
    render_or_panic("replication_wizard.html", &ctx);
}

#[test]
fn render_docs() {
    let mut ctx = base_ctx();
    ctx.insert("api_base", &"http://127.0.0.1:9000");
    ctx.insert("api_host", &"127.0.0.1:9000");
    render_or_panic("docs.html", &ctx);
}

#[test]
fn render_bucket_detail() {
    let mut ctx = base_ctx();
    ctx.insert("bucket_name", &"my-bucket");
    ctx.insert(
        "bucket",
        &json!({
            "name": "my-bucket",
            "creation_date": "2024-01-01T00:00:00Z"
        }),
    );
    ctx.insert("objects", &Vec::<Value>::new());
    ctx.insert("prefixes", &Vec::<Value>::new());
    ctx.insert("total_objects", &0);
    ctx.insert("total_bytes", &0);
    ctx.insert("current_objects", &0);
    ctx.insert("current_bytes", &0);
    ctx.insert("version_count", &0);
    ctx.insert("version_bytes", &0);
    ctx.insert("max_objects", &Value::Null);
    ctx.insert("max_bytes", &Value::Null);
    ctx.insert("obj_pct", &0);
    ctx.insert("bytes_pct", &0);
    ctx.insert("has_quota", &false);
    ctx.insert("versioning_enabled", &false);
    ctx.insert("versioning_suspended", &false);
    ctx.insert("versioning_status", &"Disabled");
    ctx.insert("encryption_config", &json!({"Rules": []}));
    ctx.insert("enc_rules", &Vec::<Value>::new());
    ctx.insert("enc_algorithm", &"");
    ctx.insert("enc_kms_key", &"");
    ctx.insert("replication_rules", &Vec::<Value>::new());
    ctx.insert("replication_rule", &Value::Null);
    ctx.insert("website_config", &Value::Null);
    ctx.insert("bucket_policy", &"");
    ctx.insert("bucket_policy_text", &"");
    ctx.insert("connections", &Vec::<Value>::new());
    ctx.insert("current_prefix", &"");
    ctx.insert("parent_prefix", &"");
    ctx.insert("has_more", &false);
    ctx.insert("next_token", &"");
    ctx.insert("active_tab", &"objects");
    ctx.insert("multipart_uploads", &Vec::<Value>::new());
    ctx.insert("target_conn", &Value::Null);
    ctx.insert("target_conn_name", &"");
    ctx.insert("preset_choice", &"");
    ctx.insert("default_policy", &"");
    ctx.insert("can_manage_cors", &true);
    ctx.insert("can_manage_lifecycle", &true);
    ctx.insert("can_manage_quota", &true);
    ctx.insert("can_manage_versioning", &true);
    ctx.insert("can_manage_website", &true);
    ctx.insert("can_edit_policy", &true);
    ctx.insert("is_replication_admin", &true);
    ctx.insert("lifecycle_enabled", &false);
    ctx.insert("site_sync_enabled", &false);
    ctx.insert("website_hosting_enabled", &false);
    ctx.insert("website_domains", &Vec::<Value>::new());
    ctx.insert("kms_keys", &Vec::<Value>::new());
    ctx.insert(
        "bucket_stats",
        &json!({
            "bytes": 0, "objects": 0, "total_bytes": 0, "total_objects": 0,
            "version_bytes": 0, "version_count": 0
        }),
    );
    ctx.insert(
        "bucket_quota",
        &json!({ "max_bytes": null, "max_objects": null }),
    );
    ctx.insert("buckets_for_copy_url", &"");
    ctx.insert("acl_url", &"");
    ctx.insert("cors_url", &"");
    ctx.insert("folders_url", &"");
    ctx.insert("lifecycle_url", &"");
    ctx.insert("objects_api_url", &"");
    ctx.insert("objects_stream_url", &"");
    render_or_panic("bucket_detail.html", &ctx);
}

#[test]
fn render_bucket_detail_without_error_document() {
    let mut ctx = base_ctx();
    ctx.insert("bucket_name", &"site-bucket");
    ctx.insert(
        "bucket",
        &json!({
            "name": "site-bucket",
            "creation_date": "2025-01-01T00:00:00Z",
        }),
    );
    ctx.insert("objects", &Vec::<Value>::new());
    ctx.insert("prefixes", &Vec::<Value>::new());
    ctx.insert("total_objects", &0u64);
    ctx.insert("total_bytes", &0u64);
    ctx.insert("current_objects", &0u64);
    ctx.insert("current_bytes", &0u64);
    ctx.insert("version_count", &0u64);
    ctx.insert("version_bytes", &0u64);
    ctx.insert("max_objects", &Value::Null);
    ctx.insert("max_bytes", &Value::Null);
    ctx.insert("has_max_objects", &false);
    ctx.insert("has_max_bytes", &false);
    ctx.insert("obj_pct", &0);
    ctx.insert("bytes_pct", &0);
    ctx.insert("has_quota", &false);
    ctx.insert("versioning_enabled", &false);
    ctx.insert("versioning_suspended", &false);
    ctx.insert("versioning_status", &"Disabled");
    ctx.insert("encryption_config", &json!({"Rules": []}));
    ctx.insert("enc_rules", &Vec::<Value>::new());
    ctx.insert("enc_algorithm", &"");
    ctx.insert("enc_kms_key", &"");
    ctx.insert("replication_rules", &Vec::<Value>::new());
    ctx.insert("replication_rule", &Value::Null);
    ctx.insert("website_config", &json!({"index_document": "index.html"}));
    ctx.insert("bucket_policy", &"");
    ctx.insert("bucket_policy_text", &"");
    ctx.insert("connections", &Vec::<Value>::new());
    ctx.insert("current_prefix", &"");
    ctx.insert("parent_prefix", &"");
    ctx.insert("has_more", &false);
    ctx.insert("next_token", &"");
    ctx.insert("active_tab", &"objects");
    ctx.insert("multipart_uploads", &Vec::<Value>::new());
    ctx.insert("target_conn", &Value::Null);
    ctx.insert("target_conn_name", &"");
    ctx.insert("preset_choice", &"");
    ctx.insert("default_policy", &"");
    ctx.insert("can_manage_cors", &true);
    ctx.insert("can_manage_lifecycle", &true);
    ctx.insert("can_manage_quota", &true);
    ctx.insert("can_manage_versioning", &true);
    ctx.insert("can_manage_website", &true);
    ctx.insert("can_edit_policy", &true);
    ctx.insert("is_replication_admin", &true);
    ctx.insert("lifecycle_enabled", &false);
    ctx.insert("site_sync_enabled", &false);
    ctx.insert("website_hosting_enabled", &true);
    ctx.insert("website_domains", &Vec::<Value>::new());
    ctx.insert("kms_keys", &Vec::<Value>::new());
    ctx.insert(
        "bucket_stats",
        &json!({
            "bytes": 0, "objects": 0, "total_bytes": 0, "total_objects": 0,
            "version_bytes": 0, "version_count": 0
        }),
    );
    ctx.insert(
        "bucket_quota",
        &json!({ "max_bytes": null, "max_objects": null }),
    );
    ctx.insert("buckets_for_copy_url", &"");
    ctx.insert("acl_url", &"");
    ctx.insert("cors_url", &"");
    ctx.insert("folders_url", &"");
    ctx.insert("lifecycle_url", &"");
    ctx.insert("objects_api_url", &"");
    ctx.insert("objects_stream_url", &"");
    render_or_panic("bucket_detail.html", &ctx);
}
