use axum::{Json, extract::State};
use serde::{Deserialize, Serialize};
use serde_json::json;
use tranquil_pds::api::ApiError;
use tranquil_pds::api::error::DbResultExt;
use tranquil_pds::auth::{Active, Auth};
use tranquil_pds::state::AppState;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VerificationMethod {
    pub id: String,
    #[serde(rename = "type")]
    pub method_type: String,
    pub public_key_multibase: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateDidDocumentInput {
    pub verification_methods: Option<Vec<VerificationMethod>>,
    pub also_known_as: Option<Vec<String>>,
    pub service_endpoint: Option<String>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateDidDocumentOutput {
    pub success: bool,
    pub did_document: serde_json::Value,
}

pub async fn update_did_document(
    State(state): State<AppState>,
    auth: Auth<Active>,
    Json(input): Json<UpdateDidDocumentInput>,
) -> Result<Json<UpdateDidDocumentOutput>, ApiError> {
    if !auth.did.starts_with("did:web:") {
        return Err(ApiError::InvalidRequest(
            "DID document updates are only available for did:web accounts".into(),
        ));
    }

    let user = state
        .repos
        .user
        .get_user_for_did_doc(&auth.did)
        .await
        .log_db_err("getting user")?
        .ok_or(ApiError::AccountNotFound)?;

    if let Some(ref methods) = input.verification_methods {
        if methods.is_empty() {
            return Err(ApiError::InvalidRequest(
                "verification_methods cannot be empty".into(),
            ));
        }
        let validation_error = methods.iter().find_map(|method| {
            if method.id.is_empty() {
                Some("verification method id is required")
            } else if method.method_type != "Multikey" {
                Some("verification method type must be 'Multikey'")
            } else if !method.public_key_multibase.starts_with('z') {
                Some("publicKeyMultibase must start with 'z' (base58btc)")
            } else if method.public_key_multibase.len() < 40 {
                Some("publicKeyMultibase appears too short for a valid key")
            } else {
                None
            }
        });
        if let Some(err) = validation_error {
            return Err(ApiError::InvalidRequest(err.into()));
        }
    }

    if let Some(ref handles) = input.also_known_as
        && handles.iter().any(|h| !h.starts_with("at://"))
    {
        return Err(ApiError::InvalidRequest(
            "alsoKnownAs entries must be at:// URIs".into(),
        ));
    }

    if let Some(ref endpoint) = input.service_endpoint {
        let endpoint = endpoint.trim();
        if !endpoint.starts_with("https://") {
            return Err(ApiError::InvalidRequest(
                "serviceEndpoint must start with https://".into(),
            ));
        }
    }

    let verification_methods_json = input
        .verification_methods
        .as_ref()
        .map(|v| serde_json::to_value(v).unwrap_or_default());

    let also_known_as: Option<Vec<String>> = input.also_known_as.clone();

    state
        .repos
        .user
        .upsert_did_web_overrides(user.id, verification_methods_json, also_known_as)
        .await
        .log_db_err("upserting did_web_overrides")?;

    if let Some(ref endpoint) = input.service_endpoint {
        let endpoint_clean = endpoint.trim().trim_end_matches('/');
        state
            .repos
            .user
            .update_migrated_to_pds(&auth.did, endpoint_clean)
            .await
            .log_db_err("updating service endpoint")?;
    }

    let did_doc = build_did_document(&state, &auth.did).await;

    tracing::info!("Updated DID document for {}", &auth.did);

    Ok(Json(UpdateDidDocumentOutput {
        success: true,
        did_document: did_doc,
    }))
}

pub async fn get_did_document(
    State(state): State<AppState>,
    auth: Auth<Active>,
) -> Result<Json<serde_json::Value>, ApiError> {
    if !auth.did.starts_with("did:web:") {
        return Err(ApiError::InvalidRequest(
            "This endpoint is only available for did:web accounts".into(),
        ));
    }

    let did_doc = build_did_document(&state, &auth.did).await;

    Ok(Json(serde_json::json!({ "didDocument": did_doc })))
}

async fn build_did_document(state: &AppState, did: &tranquil_pds::types::Did) -> serde_json::Value {
    let hostname = &tranquil_config::get().server.hostname;

    let user = match state.repos.user.get_user_for_did_doc_build(did).await {
        Ok(Some(row)) => row,
        _ => {
            return json!({
                "error": "User not found"
            });
        }
    };

    let overrides = state
        .repos
        .user
        .get_did_web_overrides(user.id)
        .await
        .ok()
        .flatten();

    let service_endpoint = user
        .migrated_to_pds
        .unwrap_or_else(|| format!("https://{}", hostname));

    if let Some((ovr, parsed)) = overrides.as_ref().and_then(|ovr| {
        serde_json::from_value::<Vec<VerificationMethod>>(ovr.verification_methods.clone())
            .ok()
            .filter(|p| !p.is_empty())
            .map(|p| (ovr, p))
    }) {
        let also_known_as = if !ovr.also_known_as.is_empty() {
            ovr.also_known_as.clone()
        } else {
            vec![format!("at://{}", user.handle)]
        };
        return json!({
            "@context": [
                "https://www.w3.org/ns/did/v1",
                "https://w3id.org/security/multikey/v1",
                "https://w3id.org/security/suites/secp256k1-2019/v1"
            ],
            "id": did,
            "alsoKnownAs": also_known_as,
            "verificationMethod": parsed.iter().map(|m| json!({
                "id": format!("{}{}", did, if m.id.starts_with('#') { m.id.clone() } else { format!("#{}", m.id) }),
                "type": m.method_type,
                "controller": did,
                "publicKeyMultibase": m.public_key_multibase
            })).collect::<Vec<_>>(),
            "service": [{
                "id": "#atproto_pds",
                "type": tranquil_pds::plc::ServiceType::Pds.as_str(),
                "serviceEndpoint": service_endpoint
            }]
        });
    }

    let key_info = state
        .repos
        .user
        .get_user_key_by_id(user.id)
        .await
        .ok()
        .flatten();

    let public_key_multibase = match key_info {
        Some(info) => {
            match tranquil_pds::config::decrypt_key(&info.key_bytes, info.encryption_version) {
                Ok(key_bytes) => crate::identity::did::get_public_key_multibase(&key_bytes)
                    .unwrap_or_else(|_| "error".to_string()),
                Err(_) => "error".to_string(),
            }
        }
        None => "error".to_string(),
    };

    let also_known_as = if let Some(ref ovr) = overrides {
        if !ovr.also_known_as.is_empty() {
            ovr.also_known_as.clone()
        } else {
            vec![format!("at://{}", user.handle)]
        }
    } else {
        vec![format!("at://{}", user.handle)]
    };

    json!({
        "@context": [
            "https://www.w3.org/ns/did/v1",
            "https://w3id.org/security/multikey/v1",
            "https://w3id.org/security/suites/secp256k1-2019/v1"
        ],
        "id": did,
        "alsoKnownAs": also_known_as,
        "verificationMethod": [{
            "id": format!("{}#atproto", did),
            "type": "Multikey",
            "controller": did,
            "publicKeyMultibase": public_key_multibase
        }],
        "service": [{
            "id": "#atproto_pds",
            "type": tranquil_pds::plc::ServiceType::Pds.as_str(),
            "serviceEndpoint": service_endpoint
        }]
    })
}
