// Admin-API for API-nøkkelhåndtering (oppgave 060)
//
// CRUD for krypterte API-nøkler i PG. Nøkler krypteres med AES-256-GCM
// via SYNOPS_MASTER_KEY. Kun hint (siste 4 tegn) returneres til frontend.
//
// Ref: docs/infra/nøkkelhåndtering.md

use axum::{extract::State, http::StatusCode, Json};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::auth::AdminUser;
use crate::crypto;
use crate::AppState;

// =============================================================================
// Datatyper
// =============================================================================

#[derive(Serialize)]
pub struct ErrorResponse {
    pub error: String,
}

fn bad_request(msg: &str) -> (StatusCode, Json<ErrorResponse>) {
    (StatusCode::BAD_REQUEST, Json(ErrorResponse { error: msg.to_string() }))
}

fn internal_error(msg: &str) -> (StatusCode, Json<ErrorResponse>) {
    (StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: msg.to_string() }))
}

/// En API-nøkkel slik den vises i admin-UI (uten selve nøkkelen).
#[derive(Serialize, sqlx::FromRow)]
pub struct ApiKeyInfo {
    pub id: Uuid,
    pub provider: String,
    pub label: Option<String>,
    pub key_hint: Option<String>,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub last_used: Option<DateTime<Utc>>,
    pub usage_count: i64,
}

// Kjente providers med test-URL og modell for tilkoblingstest
struct ProviderTestConfig {
    base_url: &'static str,
    model_list_path: &'static str,
    auth_header: &'static str,
}

fn provider_test_config(provider: &str) -> Option<ProviderTestConfig> {
    match provider {
        "openrouter" => Some(ProviderTestConfig {
            base_url: "https://openrouter.ai/api/v1",
            model_list_path: "/models",
            auth_header: "Bearer",
        }),
        "anthropic" => Some(ProviderTestConfig {
            base_url: "https://api.anthropic.com/v1",
            model_list_path: "/models",
            auth_header: "x-api-key",
        }),
        "openai" => Some(ProviderTestConfig {
            base_url: "https://api.openai.com/v1",
            model_list_path: "/models",
            auth_header: "Bearer",
        }),
        "gemini" => Some(ProviderTestConfig {
            base_url: "https://generativelanguage.googleapis.com/v1beta",
            model_list_path: "/models",
            auth_header: "x-goog-api-key",
        }),
        _ => None,
    }
}

// =============================================================================
// GET /admin/api-keys — liste alle nøkler
// =============================================================================

#[derive(Serialize)]
pub struct ApiKeysResponse {
    pub keys: Vec<ApiKeyInfo>,
}

pub async fn list_keys(
    State(state): State<AppState>,
    _admin: AdminUser,
) -> Result<Json<ApiKeysResponse>, (StatusCode, Json<ErrorResponse>)> {
    let keys = sqlx::query_as::<_, ApiKeyInfo>(
        r#"SELECT id, provider, label, key_hint, is_active,
                  created_at, updated_at, last_used, usage_count
           FROM api_keys
           ORDER BY provider, created_at DESC"#,
    )
    .fetch_all(&state.db)
    .await
    .map_err(|e| internal_error(&format!("Feil ved henting av API-nøkler: {e}")))?;

    Ok(Json(ApiKeysResponse { keys }))
}

// =============================================================================
// POST /admin/api-keys/create — opprett ny nøkkel
// =============================================================================

#[derive(Deserialize)]
pub struct CreateKeyRequest {
    pub provider: String,
    pub label: Option<String>,
    pub api_key: String,
}

#[derive(Serialize)]
pub struct CreateKeyResponse {
    pub id: Uuid,
    pub key_hint: String,
}

pub async fn create_key(
    State(state): State<AppState>,
    admin: AdminUser,
    Json(req): Json<CreateKeyRequest>,
) -> Result<Json<CreateKeyResponse>, (StatusCode, Json<ErrorResponse>)> {
    let provider = req.provider.trim().to_lowercase();
    let api_key = req.api_key.trim();

    if provider.is_empty() {
        return Err(bad_request("Provider er påkrevd"));
    }
    if api_key.is_empty() {
        return Err(bad_request("API-nøkkel er påkrevd"));
    }

    let master = crypto::master_key()
        .map_err(|e| internal_error(&e))?;

    let encrypted = crypto::encrypt(api_key, &master)
        .map_err(|e| internal_error(&e))?;

    let hint = crypto::key_hint(api_key);
    let id = Uuid::now_v7();

    sqlx::query(
        r#"INSERT INTO api_keys (id, provider, label, key_encrypted, key_hint, created_by)
           VALUES ($1, $2, $3, $4, $5, $6)"#,
    )
    .bind(id)
    .bind(&provider)
    .bind(&req.label)
    .bind(&encrypted)
    .bind(&hint)
    .bind(admin.node_id)
    .execute(&state.db)
    .await
    .map_err(|e| internal_error(&format!("Feil ved lagring av nøkkel: {e}")))?;

    tracing::info!(
        provider = %provider,
        key_hint = %hint,
        "API-nøkkel opprettet"
    );

    Ok(Json(CreateKeyResponse { id, key_hint: hint }))
}

// =============================================================================
// POST /admin/api-keys/test — test nøkkel-tilkobling
// =============================================================================

#[derive(Deserialize)]
pub struct TestKeyRequest {
    pub provider: String,
    pub api_key: String,
}

#[derive(Serialize)]
pub struct TestKeyResponse {
    pub success: bool,
    pub message: String,
}

pub async fn test_key(
    _admin: AdminUser,
    Json(req): Json<TestKeyRequest>,
) -> Result<Json<TestKeyResponse>, (StatusCode, Json<ErrorResponse>)> {
    let provider = req.provider.trim().to_lowercase();
    let api_key = req.api_key.trim();

    let config = match provider_test_config(&provider) {
        Some(c) => c,
        None => {
            return Ok(Json(TestKeyResponse {
                success: true,
                message: format!("Ingen test tilgjengelig for provider '{provider}' — nøkkel lagres uten validering"),
            }));
        }
    };

    let url = format!("{}{}", config.base_url, config.model_list_path);
    let client = reqwest::Client::new();

    let mut request = client.get(&url);
    if config.auth_header == "Bearer" {
        request = request.bearer_auth(api_key);
    } else if config.auth_header == "x-api-key" {
        request = request.header("x-api-key", api_key);
    } else if config.auth_header == "x-goog-api-key" {
        request = request.header("x-goog-api-key", api_key);
    }

    // Anthropic krever versjon-header
    if provider == "anthropic" {
        request = request.header("anthropic-version", "2023-06-01");
    }

    let response = request
        .timeout(std::time::Duration::from_secs(10))
        .send()
        .await
        .map_err(|e| {
            Ok::<_, (StatusCode, Json<ErrorResponse>)>(Json(TestKeyResponse {
                success: false,
                message: format!("Tilkoblingsfeil: {e}"),
            }))
        });

    match response {
        Ok(res) => {
            if res.status().is_success() {
                Ok(Json(TestKeyResponse {
                    success: true,
                    message: format!("Tilkobling OK ({} {})", provider, res.status()),
                }))
            } else {
                let status = res.status();
                let body = res.text().await.unwrap_or_default();
                let msg = if body.len() > 200 { &body[..200] } else { &body };
                Ok(Json(TestKeyResponse {
                    success: false,
                    message: format!("Feil {status}: {msg}"),
                }))
            }
        }
        Err(Ok(test_response)) => Ok(test_response),
        Err(Err(e)) => Err(e),
    }
}

// =============================================================================
// POST /admin/api-keys/deactivate — deaktiver nøkkel
// =============================================================================

#[derive(Deserialize)]
pub struct DeactivateKeyRequest {
    pub id: Uuid,
}

#[derive(Serialize)]
pub struct DeactivateKeyResponse {
    pub is_active: bool,
}

pub async fn deactivate_key(
    State(state): State<AppState>,
    _admin: AdminUser,
    Json(req): Json<DeactivateKeyRequest>,
) -> Result<Json<DeactivateKeyResponse>, (StatusCode, Json<ErrorResponse>)> {
    let result = sqlx::query(
        "UPDATE api_keys SET is_active = NOT is_active, updated_at = now() WHERE id = $1",
    )
    .bind(req.id)
    .execute(&state.db)
    .await
    .map_err(|e| internal_error(&format!("Feil ved oppdatering: {e}")))?;

    if result.rows_affected() == 0 {
        return Err(bad_request("Nøkkel finnes ikke"));
    }

    let is_active: bool = sqlx::query_scalar("SELECT is_active FROM api_keys WHERE id = $1")
        .bind(req.id)
        .fetch_one(&state.db)
        .await
        .map_err(|e| internal_error(&format!("DB-feil: {e}")))?;

    tracing::info!(key_id = %req.id, is_active, "API-nøkkel status endret");

    Ok(Json(DeactivateKeyResponse { is_active }))
}

// =============================================================================
// POST /admin/api-keys/delete — slett nøkkel permanent
// =============================================================================

#[derive(Deserialize)]
pub struct DeleteKeyRequest {
    pub id: Uuid,
}

#[derive(Serialize)]
pub struct DeleteKeyResponse {
    pub deleted: bool,
}

pub async fn delete_key(
    State(state): State<AppState>,
    _admin: AdminUser,
    Json(req): Json<DeleteKeyRequest>,
) -> Result<Json<DeleteKeyResponse>, (StatusCode, Json<ErrorResponse>)> {
    let result = sqlx::query("DELETE FROM api_keys WHERE id = $1")
        .bind(req.id)
        .execute(&state.db)
        .await
        .map_err(|e| internal_error(&format!("Feil ved sletting: {e}")))?;

    tracing::info!(key_id = %req.id, "API-nøkkel slettet");

    Ok(Json(DeleteKeyResponse {
        deleted: result.rows_affected() > 0,
    }))
}
