use cloud_terrastodon_azure_types::AzureTenantId;
use cloud_terrastodon_azure_types::EntraServicePrincipalId;
use cloud_terrastodon_azure_types::ScopeImpl;
use cloud_terrastodon_azure_types::serde_helpers::deserialize_none_if_empty_string;
use compact_str::CompactString;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(tag = "scheme", content = "parameters")]
pub enum AzureDevOpsServiceEndpointAuthorization {
    ServicePrincipal(AzureDevOpsServiceEndpointAuthorizationServicePrincipal),
    UsernamePassword(AzureDevOpsServiceEndpointAuthorizationUsernamePassword),
    WorkloadIdentityFederation(AzureDevOpsServiceEndpointAuthorizationWorkloadIdentityFederation),
    /// For stuff like Azure Container Registry authorization
    ManagedServiceIdentity(AzureDevOpsServiceEndpointAuthorizationManagedServiceIdentity),
    #[serde(untagged)] // https://github.com/serde-rs/serde/issues/912#issuecomment-1868785603
    Other(serde_json::Value),
}
impl AzureDevOpsServiceEndpointAuthorization {
    pub fn service_principal_id(&self) -> Option<&EntraServicePrincipalId> {
        match self {
            AzureDevOpsServiceEndpointAuthorization::ServicePrincipal(data) => {
                Some(&data.service_principal_id)
            }
            AzureDevOpsServiceEndpointAuthorization::WorkloadIdentityFederation(data) => {
                data.service_principal_id.as_ref()
            }
            _ => None,
        }
    }
}

// ============

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash)]
pub struct AzureDevOpsServiceEndpointAuthorizationServicePrincipal {
    #[serde(rename = "authenticationType")]
    pub authentication_type:
        AzureDevOpsServiceEndpointAuthorizationServicePrincipalAuthenticationType,
    #[serde(rename = "serviceprincipalid")]
    pub service_principal_id: EntraServicePrincipalId,
    #[serde(rename = "tenantId")]
    #[serde(alias = "tenantid")]
    pub tenant_id: AzureTenantId,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub enum AzureDevOpsServiceEndpointAuthorizationServicePrincipalAuthenticationType {
    #[serde(rename = "spnKey")]
    SpnKey,
}

// ============

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct AzureDevOpsServiceEndpointAuthorizationUsernamePassword {
    pub username: CompactString,
    #[serde(flatten)]
    pub extra: HashMap<CompactString, CompactString>,
}

// ============

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct AzureDevOpsServiceEndpointAuthorizationWorkloadIdentityFederation {
    pub scope: Option<ScopeImpl>,
    #[serde(rename = "servicePrincipalId")]
    #[serde(alias = "serviceprincipalid")]
    #[serde(default)]
    #[serde(deserialize_with = "deserialize_none_if_empty_string")]
    pub service_principal_id: Option<EntraServicePrincipalId>,
    #[serde(rename = "tenantId")]
    #[serde(alias = "tenantid")]
    pub tenant_id: AzureTenantId,
    #[serde(rename = "workloadIdentityFederationIssuer")]
    pub workload_identity_federation_issuer: String,
    #[serde(rename = "workloadIdentityFederationSubject")]
    pub workload_identity_federation_subject: String,
    #[serde(rename = "workloadIdentityFederationIssuerType")]
    pub workload_identity_federation_issuer_type: Option<CompactString>,
}

// ============

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct AzureDevOpsServiceEndpointAuthorizationManagedServiceIdentity {
    #[serde(rename = "tenantId")]
    #[serde(alias = "tenantid")]
    pub tenant_id: AzureTenantId,
    /// For stuff like Azure Container Registry authorization
    /// "loginServer": "pipelineimage.azurecr.io",
    #[serde(flatten)]
    pub extra: HashMap<CompactString, CompactString>,
}
