use rouille::{input::json_input, Request, Response};
use serde::Deserialize;
use serde_json::json;
use std::sync::Arc;

use super::helpers::{get_conn, json_error, require_auth};
use super::AppState;

/// Public endpoint — no auth required (setup wizard needs it before first login).
pub fn get_status(_request: &Request, state: &Arc<AppState>) -> Response {
    let mut conn = match get_conn(state) {
        Ok(c) => c,
        Err(e) => return e,
    };

    let plesk_configured = conn
        .query_opt("SELECT 1 FROM config WHERE key = 'plesk.config'", &[])
        .ok()
        .flatten()
        .is_some()
        || conn
            .query_opt("SELECT 1 FROM plesk_servers LIMIT 1", &[])
            .ok()
            .flatten()
            .is_some();

    let sap_configured = conn
        .query_opt("SELECT 1 FROM config WHERE key = 'sap.config'", &[])
        .ok()
        .flatten()
        .is_some()
        || conn
            .query_opt("SELECT 1 FROM sap_servers LIMIT 1", &[])
            .ok()
            .flatten()
            .is_some();

    Response::json(&json!({
        "plesk_configured": plesk_configured,
        "sap_configured": sap_configured,
        "setup_complete": plesk_configured && sap_configured,
    }))
}

pub fn save_config(request: &Request, state: &Arc<AppState>) -> Response {
    let mut conn = match get_conn(state) {
        Ok(c) => c,
        Err(e) => return e,
    };
    if let Err(e) = require_auth(request, &mut conn) {
        return e;
    }

    let config: sap_sync_backend::models::SetupConfig = match json_input(request) {
        Ok(f) => f,
        Err(_) => return json_error(400, "Invalid JSON"),
    };

    persist_setup(&mut conn, &config);
    Response::json(&json!({"message": "System configured successfully"}))
}

pub fn test_plesk(request: &Request, state: &Arc<AppState>) -> Response {
    let mut conn = match get_conn(state) {
        Ok(c) => c,
        Err(e) => return e,
    };
    if let Err(e) = require_auth(request, &mut conn) {
        return e;
    }

    #[derive(Deserialize)]
    struct Form {
        host: String,
        port: Option<u16>,
        username: Option<String>,
        password: Option<String>,
        api_key: Option<String>,
        #[allow(dead_code)]
        session_id: Option<String>,
    }

    let form: Form = match json_input(request) {
        Ok(f) => f,
        Err(_) => return json_error(400, "Invalid JSON"),
    };

    let port = form.port.unwrap_or(8443);
    let url = format!("https://{}:{}/api/v2/server", form.host, port);
    let mut req = ureq::get(&url);

    if let Some(ref key) = form.api_key {
        if !key.is_empty() {
            req = req.header("X-API-Key", key);
        }
    } else if let (Some(ref user), Some(ref pass)) = (form.username, form.password) {
        let creds = base64::Engine::encode(
            &base64::engine::general_purpose::STANDARD,
            format!("{}:{}", user, pass),
        );
        req = req.header("Authorization", &format!("Basic {}", creds));
    }

    match req.call() {
        Ok(resp) => {
            if resp.status() == ureq::http::StatusCode::OK
                || resp.status() == ureq::http::StatusCode::CREATED
            {
                Response::json(&json!({"success": true, "message": "Plesk connection successful"}))
            } else {
                Response::json(&json!({"success": false, "error": format!("Plesk returned status {}", u16::from(resp.status()))}))
            }
        }
        Err(e) => Response::json(&json!({"success": false, "error": format!("Connection failed: {}", e)})),
    }
}

pub fn plesk_2fa(request: &Request, state: &Arc<AppState>) -> Response {
    let mut conn = match get_conn(state) {
        Ok(c) => c,
        Err(e) => return e,
    };
    if let Err(e) = require_auth(request, &mut conn) {
        return e;
    }

    #[derive(Deserialize)]
    struct Form {
        code: String,
        host: String,
        port: Option<u16>,
        username: Option<String>,
        password: Option<String>,
        api_key: Option<String>,
        #[allow(dead_code)]
        session_id: Option<String>,
    }

    let form: Form = match json_input(request) {
        Ok(f) => f,
        Err(_) => return json_error(400, "Invalid JSON"),
    };

    let port = form.port.unwrap_or(8443);
    let url = format!("https://{}:{}/api/v2/server", form.host, port);
    let mut req = ureq::get(&url);

    if let Some(ref key) = form.api_key {
        if !key.is_empty() {
            req = req.header("X-API-Key", key);
        }
    } else if let (Some(ref user), Some(ref pass)) = (form.username, form.password) {
        let creds = base64::Engine::encode(
            &base64::engine::general_purpose::STANDARD,
            format!("{}:{}", user, pass),
        );
        req = req.header("Authorization", &format!("Basic {}", creds));
    }
    req = req.header("X-Plesk-2FA-Code", &form.code);

    match req.call() {
        Ok(resp) => {
            if resp.status() == ureq::http::StatusCode::OK
                || resp.status() == ureq::http::StatusCode::CREATED
            {
                Response::json(&json!({"success": true, "message": "2FA verification successful"}))
            } else {
                Response::json(&json!({"success": false, "error": format!("Plesk returned status {}", u16::from(resp.status()))}))
            }
        }
        Err(e) => Response::json(&json!({"success": false, "error": format!("Connection failed: {}", e)})),
    }
}

pub fn test_sap(request: &Request, state: &Arc<AppState>) -> Response {
    let mut conn = match get_conn(state) {
        Ok(c) => c,
        Err(e) => return e,
    };
    if let Err(e) = require_auth(request, &mut conn) {
        return e;
    }

    #[derive(Deserialize)]
    struct Form {
        host: String,
        port: Option<u16>,
        company_db: Option<String>,
        username: Option<String>,
        password: Option<String>,
    }

    let form: Form = match json_input(request) {
        Ok(f) => f,
        Err(_) => return json_error(400, "Invalid JSON"),
    };

    let port = form.port.unwrap_or(50000);
    let scheme = if port == 443 || port == 50001 { "https" } else { "http" };
    let url = format!("{}://{}:{}/b1s/v1/Login", scheme, form.host, port);

    let login_body = json!({
        "CompanyDB": form.company_db.unwrap_or_default(),
        "UserName": form.username.unwrap_or_default(),
        "Password": form.password.unwrap_or_default(),
    });

    match ureq::post(&url)
        .header("Content-Type", "application/json")
        .send(login_body.to_string().as_bytes())
    {
        Ok(resp) => {
            let status: u16 = resp.status().into();
            if status == 200 {
                Response::json(&json!({"success": true, "message": "SAP connection successful"}))
            } else {
                Response::json(&json!({"success": false, "error": format!("SAP returned status {}", status)}))
            }
        }
        Err(e) => Response::json(&json!({"success": false, "error": format!("Connection failed: {}", e)})),
    }
}

// ==================== Internal ====================

fn persist_setup(
    conn: &mut postgres::Client,
    config: &sap_sync_backend::models::SetupConfig,
) {
    let plesk = serde_json::to_string(&config.plesk).unwrap_or_default();
    let sap = serde_json::to_string(&config.sap).unwrap_or_default();

    let mut upsert = |key: &str, val: &str| {
        let _ = conn.execute(
            "INSERT INTO config (key, value) VALUES ($1, $2::text::jsonb) \
             ON CONFLICT (key) DO UPDATE SET value = $2::text::jsonb",
            &[&key, &val],
        );
    };

    upsert("plesk.config", &plesk);
    upsert("sap.config", &sap);
    upsert("sync.direction", &serde_json::to_string(&config.sync.sync_direction).unwrap_or_default());
    upsert("sync.interval_minutes", &serde_json::to_string(&config.sync.sync_interval_minutes).unwrap_or_default());
    upsert("sync.conflict_resolution", &serde_json::to_string(&config.sync.conflict_resolution).unwrap_or_default());
}
