use athena_auth_core::entity::AuthPasskey;
use serde::{Deserialize, Deserializer, Serialize};
use validator::Validate;

use crate::plugins::helpers::SessionWithServerVersion;

// -- Request types --

#[cfg(feature = "axum")]
#[derive(Debug, Deserialize)]
pub(crate) struct RegisterOptionsQuery {
    #[serde(rename = "authenticatorAttachment")]
    pub(crate) authenticator_attachment: Option<String>,
}

fn string_field(value: &serde_json::Value, field: &str) -> Option<String> {
    value
        .as_object()
        .and_then(|object| object.get(field))
        .and_then(serde_json::Value::as_str)
        .map(ToOwned::to_owned)
}

fn response_payload_from_body(value: serde_json::Value) -> serde_json::Value {
    let has_top_level_id = value
        .as_object()
        .and_then(|object| object.get("id"))
        .is_some();

    if has_top_level_id {
        return value;
    }

    value
        .as_object()
        .and_then(|object| object.get("response"))
        .cloned()
        .unwrap_or(value)
}

#[derive(Debug, Validate)]
pub(crate) struct VerifyRegistrationRequest {
    pub(super) response: serde_json::Value,
    pub(super) name: Option<String>,
}

impl<'de> Deserialize<'de> for VerifyRegistrationRequest {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = serde_json::Value::deserialize(deserializer)?;
        let name = string_field(&value, "name");
        let response = response_payload_from_body(value);
        Ok(Self { response, name })
    }
}

#[derive(Debug, Validate)]
pub(crate) struct VerifyAuthenticationRequest {
    pub(super) response: serde_json::Value,
}

impl<'de> Deserialize<'de> for VerifyAuthenticationRequest {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = serde_json::Value::deserialize(deserializer)?;
        let response = response_payload_from_body(value);
        Ok(Self { response })
    }
}

#[derive(Debug, Deserialize, Validate)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DeletePasskeyRequest {
    #[validate(length(min = 1))]
    pub(super) id: String,
}

#[derive(Debug, Deserialize, Validate)]
#[serde(rename_all = "camelCase")]
pub(crate) struct UpdatePasskeyRequest {
    #[validate(length(min = 1))]
    pub(super) id: String,
    #[validate(length(min = 1))]
    pub(super) name: String,
}

// -- Response helpers --

#[derive(Debug, Serialize)]
pub(crate) struct PasskeyView {
    id: String,
    name: String,
    #[serde(rename = "credentialID")]
    credential_id: String,
    #[serde(rename = "userId")]
    user_id: String,
    #[serde(rename = "publicKey")]
    public_key: String,
    counter: u64,
    #[serde(rename = "deviceType")]
    device_type: String,
    #[serde(rename = "backedUp")]
    backed_up: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    transports: Option<String>,
    #[serde(rename = "createdAt")]
    created_at: String,
}

impl PasskeyView {
    pub(super) fn from_entity(pk: &impl AuthPasskey) -> Self {
        Self {
            id: pk.id().to_string(),
            name: pk.name().to_string(),
            credential_id: pk.credential_id().to_string(),
            user_id: pk.user_id().to_string(),
            public_key: pk.public_key().to_string(),
            counter: pk.counter(),
            device_type: pk.device_type().to_string(),
            backed_up: pk.backed_up(),
            transports: pk.transports().map(|s| s.to_string()),
            created_at: pk.created_at().to_rfc3339(),
        }
    }
}

#[derive(Debug, Serialize)]
pub(crate) struct SessionUserResponse<U: Serialize, S: Serialize> {
    pub(crate) session: SessionWithServerVersion<S>,
    pub(crate) user: U,
}

#[derive(Debug, Serialize)]
pub(crate) struct PasskeyResponse {
    pub(super) passkey: PasskeyView,
}
