use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
use validator::Validate;

// Re-export organization types
pub use super::types_org::{
    CreateInvitation, CreateMember, CreateOrganization, FullOrganization, Invitation,
    InvitationStatus, Member, MemberUser, MemberWithUser, Organization, UpdateOrganization,
};

/// Core user type - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct User {
    pub id: String,
    pub name: Option<String>,
    pub email: Option<String>,
    #[serde(rename = "emailVerified")]
    pub email_verified: bool,
    pub image: Option<String>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
    pub username: Option<String>,
    #[serde(rename = "displayUsername")]
    pub display_username: Option<String>,
    #[serde(rename = "twoFactorEnabled")]
    pub two_factor_enabled: bool,
    pub role: Option<String>,
    pub banned: bool,
    #[serde(rename = "banReason")]
    pub ban_reason: Option<String>,
    #[serde(rename = "banExpires")]
    pub ban_expires: Option<DateTime<Utc>>,
    #[serde(rename = "lastSignInAt")]
    pub last_sign_in_at: Option<DateTime<Utc>>,
    // Keep metadata for internal use but don't serialize
    #[serde(skip)]
    pub metadata: serde_json::Value,
}

/// Session information - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
    pub id: String,
    #[serde(rename = "expiresAt")]
    pub expires_at: DateTime<Utc>,
    pub token: String,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
    #[serde(rename = "ipAddress")]
    pub ip_address: Option<String>,
    #[serde(rename = "userAgent")]
    pub user_agent: Option<String>,
    #[serde(rename = "userId")]
    pub user_id: String,
    #[serde(rename = "impersonatedBy")]
    pub impersonated_by: Option<String>,
    #[serde(rename = "activeOrganizationId")]
    pub active_organization_id: Option<String>,
    // Keep active field for internal use but don't serialize
    #[serde(skip)]
    pub active: bool,
}

/// Account linking (for OAuth providers) - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Account {
    pub id: String,
    #[serde(rename = "accountId")]
    pub account_id: String,
    #[serde(rename = "providerId")]
    pub provider_id: String,
    #[serde(rename = "userId")]
    pub user_id: String,
    #[serde(rename = "accessToken")]
    pub access_token: Option<String>,
    #[serde(rename = "refreshToken")]
    pub refresh_token: Option<String>,
    #[serde(rename = "idToken")]
    pub id_token: Option<String>,
    #[serde(rename = "accessTokenExpiresAt")]
    pub access_token_expires_at: Option<DateTime<Utc>>,
    #[serde(rename = "refreshTokenExpiresAt")]
    pub refresh_token_expires_at: Option<DateTime<Utc>>,
    pub scope: Option<String>,
    pub password: Option<String>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Verification token - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Verification {
    pub id: String,
    pub identifier: String,
    pub value: String,
    #[serde(rename = "expiresAt")]
    pub expires_at: DateTime<Utc>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Two-factor authentication - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TwoFactor {
    pub id: String,
    pub secret: String,
    #[serde(rename = "backupCodes")]
    pub backup_codes: Option<String>,
    #[serde(rename = "userId")]
    pub user_id: String,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Passkey authentication - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Passkey {
    pub id: String,
    pub name: String,
    #[serde(rename = "publicKey")]
    pub public_key: String,
    #[serde(rename = "userId")]
    pub user_id: String,
    #[serde(rename = "credentialID")]
    pub credential_id: String,
    pub counter: u64,
    #[serde(rename = "deviceType")]
    pub device_type: String,
    #[serde(rename = "backedUp")]
    pub backed_up: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transports: Option<String>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
}

/// Input for creating a new passkey
#[derive(Debug, Clone)]
pub struct CreatePasskey {
    pub user_id: String,
    pub name: String,
    pub credential_id: String,
    pub public_key: String,
    pub counter: u64,
    pub device_type: String,
    pub backed_up: bool,
    pub transports: Option<String>,
}

/// Input for updating a passkey
#[derive(Debug, Clone)]
pub struct UpdatePasskey {
    pub name: Option<String>,
}

/// HTTP method enumeration
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum HttpMethod {
    Get,
    Post,
    Put,
    Delete,
    Patch,
    Options,
    Head,
}

/// Authentication request wrapper
#[derive(Debug, Clone)]
pub struct AuthRequest {
    pub method: HttpMethod,
    pub path: String,
    pub headers: HashMap<String, String>,
    pub body: Option<Vec<u8>>,
    pub query: HashMap<String, String>,
    /// Virtual user ID injected by a `BeforeRequestAction::InjectSession`.
    ///
    /// When set, downstream handlers treat the request as authenticated for
    /// this user **without** a real database session.  This mirrors the
    /// TypeScript `ctx.context.session` virtual-session approach.
    ///
    /// # Security
    ///
    /// This field **must only** be set by the internal request pipeline
    /// (via [`set_virtual_user_id`](AuthRequest::set_virtual_user_id)) after
    /// a plugin's `before_request` hook returns
    /// `BeforeRequestAction::InjectSession`.  Application code constructing
    /// an `AuthRequest` should always leave this as `None`; setting it to
    /// `Some(…)` externally bypasses normal authentication.
    ///
    /// **`pub(crate)`** — external crates must use [`AuthRequest::from_parts`]
    /// or [`AuthRequest::new`] (which initialise this to `None`) and then
    /// [`set_virtual_user_id`](AuthRequest::set_virtual_user_id) only from
    /// the trusted request pipeline.
    pub(crate) virtual_user_id: Option<String>,
}

/// Authentication response wrapper
#[derive(Debug, Clone)]
pub struct AuthResponse {
    pub status: u16,
    pub headers: HashMap<String, String>,
    pub body: Vec<u8>,
}

/// User creation data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateUser {
    pub id: Option<String>,
    pub email: Option<String>,
    pub name: Option<String>,
    pub image: Option<String>,
    pub email_verified: Option<bool>,
    pub password: Option<String>,
    pub username: Option<String>,
    pub display_username: Option<String>,
    pub role: Option<String>,
    pub metadata: Option<serde_json::Value>,
}

/// User update data
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UpdateUser {
    pub email: Option<String>,
    pub name: Option<String>,
    pub image: Option<String>,
    pub email_verified: Option<bool>,
    pub username: Option<String>,
    pub display_username: Option<String>,
    pub role: Option<String>,
    pub banned: Option<bool>,
    pub ban_reason: Option<String>,
    pub ban_expires: Option<DateTime<Utc>>,
    pub last_sign_in_at: Option<DateTime<Utc>>,
    pub two_factor_enabled: Option<bool>,
    pub metadata: Option<serde_json::Value>,
}

/// Session creation data
#[derive(Debug, Clone)]
pub struct CreateSession {
    pub user_id: String,
    pub expires_at: DateTime<Utc>,
    pub ip_address: Option<String>,
    pub user_agent: Option<String>,
    pub impersonated_by: Option<String>,
    pub active_organization_id: Option<String>,
}

/// Persisted IP-level profile metadata used for session intelligence.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct IpProfileRecord {
    pub id: String,
    #[serde(rename = "ipAddress")]
    pub ip_address: String,
    pub provider: String,
    #[serde(rename = "countryCode")]
    pub country_code: Option<String>,
    #[serde(rename = "countryName")]
    pub country_name: Option<String>,
    #[serde(rename = "regionName")]
    pub region_name: Option<String>,
    pub city: Option<String>,
    pub latitude: Option<f64>,
    pub longitude: Option<f64>,
    pub timezone: Option<String>,
    pub isp: Option<String>,
    pub organization: Option<String>,
    pub asn: Option<String>,
    #[serde(rename = "isVpn")]
    pub is_vpn: Option<bool>,
    #[serde(rename = "isProxy")]
    pub is_proxy: Option<bool>,
    #[serde(rename = "isTor")]
    pub is_tor: Option<bool>,
    #[serde(rename = "isHosting")]
    pub is_hosting: Option<bool>,
    #[serde(rename = "rawPayload")]
    pub raw_payload: serde_json::Value,
    #[serde(rename = "lookupExpiresAt")]
    pub lookup_expires_at: Option<DateTime<Utc>>,
    #[serde(rename = "firstSeenAt")]
    pub first_seen_at: DateTime<Utc>,
    #[serde(rename = "lastSeenAt")]
    pub last_seen_at: DateTime<Utc>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Upsert payload for IP-level profile cache.
#[derive(Debug, Clone)]
pub struct UpsertIpProfileRecord {
    pub ip_address: String,
    pub provider: String,
    pub country_code: Option<String>,
    pub country_name: Option<String>,
    pub region_name: Option<String>,
    pub city: Option<String>,
    pub latitude: Option<f64>,
    pub longitude: Option<f64>,
    pub timezone: Option<String>,
    pub isp: Option<String>,
    pub organization: Option<String>,
    pub asn: Option<String>,
    pub is_vpn: Option<bool>,
    pub is_proxy: Option<bool>,
    pub is_tor: Option<bool>,
    pub is_hosting: Option<bool>,
    pub raw_payload: serde_json::Value,
    pub lookup_expires_at: Option<DateTime<Utc>>,
}

/// Persisted per-session intelligence projection.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct SessionIpProfileRecord {
    pub id: String,
    #[serde(rename = "sessionId")]
    pub session_id: String,
    #[serde(rename = "sessionToken")]
    pub session_token: String,
    #[serde(rename = "userId")]
    pub user_id: String,
    #[serde(rename = "ipAddress")]
    pub ip_address: String,
    #[serde(rename = "ipProfileId")]
    pub ip_profile_id: Option<String>,
    #[serde(rename = "countryCode")]
    pub country_code: Option<String>,
    #[serde(rename = "countryName")]
    pub country_name: Option<String>,
    #[serde(rename = "isVpn")]
    pub is_vpn: Option<bool>,
    #[serde(rename = "isProxy")]
    pub is_proxy: Option<bool>,
    #[serde(rename = "isTor")]
    pub is_tor: Option<bool>,
    #[serde(rename = "isHosting")]
    pub is_hosting: Option<bool>,
    #[serde(rename = "countryChanged")]
    pub country_changed: bool,
    #[serde(rename = "previousCountryCode")]
    pub previous_country_code: Option<String>,
    pub provider: Option<String>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
}

/// Create payload for per-session intelligence projection.
#[derive(Debug, Clone)]
pub struct CreateSessionIpProfileRecord {
    pub session_id: String,
    pub session_token: String,
    pub user_id: String,
    pub ip_address: String,
    pub ip_profile_id: Option<String>,
    pub country_code: Option<String>,
    pub country_name: Option<String>,
    pub is_vpn: Option<bool>,
    pub is_proxy: Option<bool>,
    pub is_tor: Option<bool>,
    pub is_hosting: Option<bool>,
    pub country_changed: bool,
    pub previous_country_code: Option<String>,
    pub provider: Option<String>,
}

/// Account creation data
#[derive(Debug, Clone)]
pub struct CreateAccount {
    pub user_id: String,
    pub account_id: String,
    pub provider_id: String,
    pub access_token: Option<String>,
    pub refresh_token: Option<String>,
    pub id_token: Option<String>,
    pub access_token_expires_at: Option<DateTime<Utc>>,
    pub refresh_token_expires_at: Option<DateTime<Utc>>,
    pub scope: Option<String>,
    pub password: Option<String>,
}

/// Account update data (for refreshing OAuth tokens)
#[derive(Debug, Clone, Default)]
pub struct UpdateAccount {
    pub access_token: Option<String>,
    pub refresh_token: Option<String>,
    pub id_token: Option<String>,
    pub access_token_expires_at: Option<DateTime<Utc>>,
    pub refresh_token_expires_at: Option<DateTime<Utc>>,
    pub scope: Option<String>,
    pub password: Option<String>,
}

/// Two-factor authentication creation data
#[derive(Debug, Clone)]
pub struct CreateTwoFactor {
    pub user_id: String,
    pub secret: String,
    pub backup_codes: Option<String>,
}

/// API key - matches OpenAPI schema
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiKey {
    pub id: String,
    pub name: Option<String>,
    pub start: Option<String>,
    pub prefix: Option<String>,
    /// SHA-256 hash of the key (column name: `key` in SQL)
    #[serde(rename = "key")]
    pub key_hash: String,
    #[serde(rename = "userId")]
    pub user_id: String,
    #[serde(rename = "refillInterval")]
    pub refill_interval: Option<i64>,
    #[serde(rename = "refillAmount")]
    pub refill_amount: Option<i64>,
    #[serde(rename = "lastRefillAt")]
    pub last_refill_at: Option<String>,
    pub enabled: bool,
    #[serde(rename = "rateLimitEnabled")]
    pub rate_limit_enabled: bool,
    #[serde(rename = "rateLimitTimeWindow")]
    pub rate_limit_time_window: Option<i64>,
    #[serde(rename = "rateLimitMax")]
    pub rate_limit_max: Option<i64>,
    #[serde(rename = "requestCount")]
    pub request_count: Option<i64>,
    pub remaining: Option<i64>,
    #[serde(rename = "lastRequest")]
    pub last_request: Option<String>,
    #[serde(rename = "expiresAt")]
    pub expires_at: Option<String>,
    #[serde(rename = "createdAt")]
    pub created_at: String,
    #[serde(rename = "updatedAt")]
    pub updated_at: String,
    pub permissions: Option<String>,
    pub metadata: Option<String>,
}

/// API key creation data
#[derive(Debug, Clone)]
pub struct CreateApiKey {
    pub user_id: String,
    pub name: Option<String>,
    pub prefix: Option<String>,
    pub key_hash: String,
    pub start: Option<String>,
    pub expires_at: Option<String>,
    pub remaining: Option<i64>,
    pub rate_limit_enabled: bool,
    pub rate_limit_time_window: Option<i64>,
    pub rate_limit_max: Option<i64>,
    pub refill_interval: Option<i64>,
    pub refill_amount: Option<i64>,
    pub permissions: Option<String>,
    pub metadata: Option<String>,
    pub enabled: bool,
}

/// API key update data
#[derive(Debug, Clone, Default)]
pub struct UpdateApiKey {
    pub name: Option<String>,
    pub enabled: Option<bool>,
    pub remaining: Option<i64>,
    pub rate_limit_enabled: Option<bool>,
    pub rate_limit_time_window: Option<i64>,
    pub rate_limit_max: Option<i64>,
    pub refill_interval: Option<i64>,
    pub refill_amount: Option<i64>,
    pub permissions: Option<String>,
    pub metadata: Option<String>,
    /// Update the expiration time. `Some(Some("..."))` sets a new value,
    /// `Some(None)` clears it, `None` leaves it unchanged.
    pub expires_at: Option<Option<String>>,
    /// Last request timestamp (updated during verify).
    pub last_request: Option<Option<String>>,
    /// Request count within the current rate-limit window.
    pub request_count: Option<i64>,
    /// Last refill timestamp (updated during verify).
    pub last_refill_at: Option<Option<String>>,
}

/// Verification token creation data
#[derive(Debug, Clone)]
pub struct CreateVerification {
    pub identifier: String,
    pub value: String,
    pub expires_at: DateTime<Utc>,
}

/// Email delivery failure record creation data.
#[derive(Debug, Clone)]
pub struct CreateEmailSendFailure {
    pub user_id: Option<String>,
    pub recipient_email: String,
    pub flow: String,
    pub provider: Option<String>,
    pub error_message: String,
    pub metadata: Option<serde_json::Value>,
}

/// Persisted record of a successfully sent email (audit / support).
#[derive(Debug, Clone)]
pub struct CreateSentEmail {
    pub recipient_email: String,
    pub subject: String,
    pub from_address: String,
    pub from_name: Option<String>,
    pub text_body: Option<String>,
    pub html_body: Option<String>,
    pub provider: String,
    pub flow: Option<String>,
    pub metadata: Option<serde_json::Value>,
}

/// Stored email delivery record from the `emails` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct SentEmail {
    pub id: String,
    #[serde(rename = "recipientEmail")]
    pub recipient_email: String,
    pub subject: String,
    #[serde(rename = "fromAddress")]
    pub from_address: String,
    #[serde(rename = "fromName")]
    pub from_name: Option<String>,
    #[serde(rename = "textBody")]
    pub text_body: Option<String>,
    #[serde(rename = "htmlBody")]
    pub html_body: Option<String>,
    pub provider: String,
    pub flow: Option<String>,
    pub metadata: serde_json::Value,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Input for updating a sent email row.
#[derive(Debug, Clone, Default)]
pub struct UpdateSentEmail {
    pub recipient_email: Option<String>,
    pub subject: Option<String>,
    pub from_address: Option<String>,
    /// `None` keeps existing value, `Some(None)` clears it.
    pub from_name: Option<Option<String>>,
    /// `None` keeps existing value, `Some(None)` clears it.
    pub text_body: Option<Option<String>>,
    /// `None` keeps existing value, `Some(None)` clears it.
    pub html_body: Option<Option<String>>,
    pub provider: Option<String>,
    /// `None` keeps existing value, `Some(None)` clears it.
    pub flow: Option<Option<String>>,
    pub metadata: Option<serde_json::Value>,
}

/// Query parameters used by adapter-level sent email listing.
#[derive(Debug, Clone, Default)]
pub struct ListSentEmailsParams {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub recipient_email: Option<String>,
    pub provider: Option<String>,
    pub flow: Option<String>,
    pub subject_contains: Option<String>,
    pub created_after: Option<DateTime<Utc>>,
    pub created_before: Option<DateTime<Utc>>,
}

/// Stored failure record from the `email_send_failures` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct EmailSendFailure {
    pub id: String,
    #[serde(rename = "userId")]
    pub user_id: Option<String>,
    #[serde(rename = "recipientEmail")]
    pub recipient_email: String,
    pub flow: String,
    pub provider: Option<String>,
    #[serde(rename = "errorMessage")]
    pub error_message: String,
    pub metadata: serde_json::Value,
    pub resolved: bool,
    #[serde(rename = "resolutionNote")]
    pub resolution_note: Option<String>,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Input for updating an email send failure row.
#[derive(Debug, Clone, Default)]
pub struct UpdateEmailSendFailure {
    pub resolved: Option<bool>,
    /// `None` keeps existing value, `Some(None)` clears it.
    pub resolution_note: Option<Option<String>>,
    pub metadata: Option<serde_json::Value>,
}

/// Query parameters used by adapter-level email failure listing.
#[derive(Debug, Clone, Default)]
pub struct ListEmailSendFailuresParams {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub recipient_email: Option<String>,
    pub provider: Option<String>,
    pub flow: Option<String>,
    pub resolved: Option<bool>,
    pub created_after: Option<DateTime<Utc>>,
    pub created_before: Option<DateTime<Utc>>,
}

/// Configurable email template row from the `email_templates` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct EmailTemplate {
    pub id: String,
    #[serde(rename = "templateKey")]
    pub template_key: String,
    #[serde(rename = "eventType")]
    pub event_type: Option<String>,
    pub locale: String,
    #[serde(rename = "subjectTemplate")]
    pub subject_template: String,
    #[serde(rename = "textTemplate")]
    pub text_template: Option<String>,
    #[serde(rename = "htmlTemplate")]
    pub html_template: Option<String>,
    pub variables: serde_json::Value,
    #[serde(rename = "variableBindings")]
    pub variable_bindings: serde_json::Value,
    #[serde(rename = "isActive")]
    pub is_active: bool,
    pub metadata: serde_json::Value,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Input for creating an email template row.
#[derive(Debug, Clone)]
pub struct CreateEmailTemplate {
    pub template_key: String,
    pub event_type: Option<String>,
    pub locale: Option<String>,
    pub subject_template: String,
    pub text_template: Option<String>,
    pub html_template: Option<String>,
    pub variables: Option<serde_json::Value>,
    pub variable_bindings: Option<serde_json::Value>,
    pub is_active: Option<bool>,
    pub metadata: Option<serde_json::Value>,
}

/// Input for updating an email template row.
#[derive(Debug, Clone, Default)]
pub struct UpdateEmailTemplate {
    pub template_key: Option<String>,
    /// `None` keeps existing value, `Some(None)` clears the value.
    pub event_type: Option<Option<String>>,
    pub locale: Option<String>,
    pub subject_template: Option<String>,
    /// `None` keeps existing value, `Some(None)` clears the value.
    pub text_template: Option<Option<String>>,
    /// `None` keeps existing value, `Some(None)` clears the value.
    pub html_template: Option<Option<String>>,
    pub variables: Option<serde_json::Value>,
    pub variable_bindings: Option<serde_json::Value>,
    pub is_active: Option<bool>,
    pub metadata: Option<serde_json::Value>,
}

/// Query parameters used by adapter-level email template listing.
#[derive(Debug, Clone, Default)]
pub struct ListEmailTemplatesParams {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub template_key: Option<String>,
    pub event_type: Option<String>,
    pub locale: Option<String>,
    pub is_active: Option<bool>,
}

/// Canonical email event type catalog row from `email_event_types`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct EmailEventType {
    #[serde(rename = "eventType")]
    pub event_type: String,
    pub category: String,
    pub description: String,
    #[serde(rename = "defaultTemplateKey")]
    pub default_template_key: Option<String>,
    #[serde(rename = "requiredVariables")]
    pub required_variables: serde_json::Value,
    #[serde(rename = "optionalVariables")]
    pub optional_variables: serde_json::Value,
    #[serde(rename = "isActive")]
    pub is_active: bool,
    #[serde(rename = "isSystem")]
    pub is_system: bool,
    pub metadata: serde_json::Value,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Query parameters used by adapter-level email event type listing.
#[derive(Debug, Clone, Default)]
pub struct ListEmailEventTypesParams {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub event_type: Option<String>,
    pub category: Option<String>,
    pub is_active: Option<bool>,
    pub is_system: Option<bool>,
}

/// Athena client registry record.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct AthenaClient {
    pub id: String,
    #[serde(rename = "clientName")]
    pub client_name: String,
    pub description: Option<String>,
    #[serde(rename = "isActive")]
    pub is_active: bool,
    #[serde(rename = "pgUriEnvVar")]
    pub pg_uri_env_var: Option<String>,
    pub metadata: serde_json::Value,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Input for creating an Athena client.
#[derive(Debug, Clone)]
pub struct CreateAthenaClient {
    pub client_name: String,
    pub description: Option<String>,
    pub is_active: Option<bool>,
    pub pg_uri_env_var: Option<String>,
    pub metadata: Option<serde_json::Value>,
}

/// OAuth client registry record used to manage allowed social-login callback URLs.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct OAuthClient {
    pub id: String,
    #[serde(rename = "clientName")]
    pub client_name: String,
    pub provider: String,
    pub description: Option<String>,
    #[serde(rename = "responseType")]
    pub response_type: String,
    #[serde(rename = "grantType")]
    pub grant_type: String,
    #[serde(rename = "tokenAuthenticationMethod")]
    pub token_authentication_method: String,
    #[serde(rename = "redirectUris")]
    pub redirect_uris: Vec<String>,
    #[serde(rename = "clientUrl")]
    pub client_url: Option<String>,
    pub scopes: Vec<String>,
    #[serde(rename = "isActive")]
    pub is_active: bool,
    pub metadata: serde_json::Value,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
    #[serde(rename = "updatedAt")]
    pub updated_at: DateTime<Utc>,
}

/// Input for creating an OAuth client registry entry.
#[derive(Debug, Clone)]
pub struct CreateOAuthClient {
    pub client_name: String,
    pub provider: String,
    pub description: Option<String>,
    pub response_type: Option<String>,
    pub grant_type: Option<String>,
    pub token_authentication_method: Option<String>,
    pub redirect_uris: Vec<String>,
    pub client_url: Option<String>,
    pub scopes: Option<Vec<String>>,
    pub is_active: Option<bool>,
    pub metadata: Option<serde_json::Value>,
}

/// Admin API audit log record.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx-postgres", derive(sqlx::FromRow))]
pub struct AdminAuditLog {
    pub id: String,
    #[serde(rename = "actorUserId")]
    pub actor_user_id: Option<String>,
    #[serde(rename = "actorSessionToken")]
    pub actor_session_token: Option<String>,
    pub action: String,
    #[serde(rename = "targetType")]
    pub target_type: Option<String>,
    #[serde(rename = "targetId")]
    pub target_id: Option<String>,
    pub success: bool,
    #[serde(rename = "errorMessage")]
    pub error_message: Option<String>,
    pub metadata: serde_json::Value,
    #[serde(rename = "createdAt")]
    pub created_at: DateTime<Utc>,
}

/// Input for creating an admin audit log entry.
#[derive(Debug, Clone)]
pub struct CreateAdminAuditLog {
    pub actor_user_id: Option<String>,
    pub actor_session_token: Option<String>,
    pub action: String,
    pub target_type: Option<String>,
    pub target_id: Option<String>,
    pub success: bool,
    pub error_message: Option<String>,
    pub metadata: Option<serde_json::Value>,
}

/// Query parameters used by adapter-level admin audit log listing.
#[derive(Debug, Clone, Default)]
pub struct ListAdminAuditLogsParams {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub actor_user_id: Option<String>,
    pub action: Option<String>,
    pub target_type: Option<String>,
    pub target_id: Option<String>,
    pub success: Option<bool>,
    pub created_after: Option<DateTime<Utc>>,
    pub created_before: Option<DateTime<Utc>>,
}

impl CreateUser {
    pub fn new() -> Self {
        Self {
            id: Some(Uuid::new_v4().to_string()),
            email: None,
            name: None,
            image: None,
            email_verified: None,
            password: None,
            username: None,
            display_username: None,
            role: None,
            metadata: None,
        }
    }

    pub fn with_email(mut self, email: impl Into<String>) -> Self {
        self.email = Some(email.into());
        self
    }

    pub fn with_name(mut self, name: impl Into<String>) -> Self {
        self.name = Some(name.into());
        self
    }

    pub fn with_image(mut self, image: impl Into<String>) -> Self {
        self.image = Some(image.into());
        self
    }

    pub fn with_email_verified(mut self, verified: bool) -> Self {
        self.email_verified = Some(verified);
        self
    }

    pub fn with_password(mut self, password: impl Into<String>) -> Self {
        self.password = Some(password.into());
        self
    }

    pub fn with_username(mut self, username: impl Into<String>) -> Self {
        self.username = Some(username.into());
        self
    }

    pub fn with_role(mut self, role: impl Into<String>) -> Self {
        self.role = Some(role.into());
        self
    }

    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
        self.metadata = Some(metadata);
        self
    }
}

impl Default for CreateUser {
    fn default() -> Self {
        Self::new()
    }
}

impl AuthRequest {
    pub fn new(method: HttpMethod, path: impl Into<String>) -> Self {
        Self {
            method,
            path: path.into(),
            headers: HashMap::new(),
            body: None,
            query: HashMap::new(),
            virtual_user_id: None,
        }
    }

    /// Construct a request from all public parts.
    ///
    /// Prefer [`AuthRequest::new`] when you only need method + path.
    pub fn from_parts(
        method: HttpMethod,
        path: String,
        headers: HashMap<String, String>,
        body: Option<Vec<u8>>,
        query: HashMap<String, String>,
    ) -> Self {
        Self {
            method,
            path,
            headers,
            body,
            query,
            virtual_user_id: None,
        }
    }

    pub fn method(&self) -> &HttpMethod {
        &self.method
    }

    pub fn path(&self) -> &str {
        &self.path
    }

    pub fn header(&self, name: &str) -> Option<&String> {
        self.headers.get(name)
    }

    pub fn header_ignore_ascii_case(&self, name: &str) -> Option<&String> {
        self.headers
            .iter()
            .find(|(header_name, _)| header_name.eq_ignore_ascii_case(name))
            .map(|(_, value)| value)
    }

    /// Returns the virtual user ID injected by a `before_request` hook, if any.
    pub fn virtual_user_id(&self) -> Option<&str> {
        self.virtual_user_id.as_deref()
    }

    /// Set the virtual user ID on this request.
    ///
    /// # Safety contract
    ///
    /// This **must only** be called from the internal request pipeline
    /// (i.e. `handle_request_inner`) after a plugin's `before_request` hook
    /// returns `BeforeRequestAction::InjectSession`.  Calling it from
    /// application code would bypass normal authentication.
    pub fn set_virtual_user_id(&mut self, user_id: String) {
        self.virtual_user_id = Some(user_id);
    }

    pub fn body_as_json<T: for<'de> Deserialize<'de>>(&self) -> Result<T, serde_json::Error> {
        if let Some(body) = &self.body {
            serde_json::from_slice(body)
        } else {
            serde_json::from_str("{}")
        }
    }
}

impl AuthResponse {
    pub fn new(status: u16) -> Self {
        Self {
            status,
            headers: HashMap::new(),
            body: Vec::new(),
        }
    }

    pub fn json<T: Serialize>(status: u16, data: &T) -> Result<Self, serde_json::Error> {
        let body = serde_json::to_vec(data)?;
        let mut headers = HashMap::new();
        headers.insert("content-type".to_string(), "application/json".to_string());

        Ok(Self {
            status,
            headers,
            body,
        })
    }

    pub fn text(status: u16, text: impl Into<String>) -> Self {
        let body = text.into().into_bytes();
        let mut headers = HashMap::new();
        headers.insert("content-type".to_string(), "text/plain".to_string());

        Self {
            status,
            headers,
            body,
        }
    }

    pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
        self.headers.insert(name.into(), value.into());
        self
    }
}

#[derive(Debug, Deserialize, Validate)]
pub struct UpdateUserRequest {
    pub name: Option<String>,
    #[validate(email(message = "Invalid email address"))]
    pub email: Option<String>,
    pub image: Option<String>,
    pub username: Option<String>,
    #[serde(rename = "displayUsername")]
    pub display_username: Option<String>,
    pub role: Option<String>,
    pub metadata: Option<serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct UpdateUserResponse<U: Serialize> {
    pub user: U,
}

/// Generic `{ ok: bool }` response used by `/ok` and `/error` endpoints.
#[derive(Debug, Serialize)]
pub struct OkResponse {
    pub ok: bool,
}

/// Generic `{ status: bool }` response.
#[derive(Debug, Serialize)]
pub struct StatusResponse {
    pub status: bool,
}

/// `{ status: bool, message: String }` response (e.g. change-email).
#[derive(Debug, Serialize)]
pub struct StatusMessageResponse {
    pub status: bool,
    pub message: String,
}

/// Generic `{ success: bool }` response (e.g. sign-out).
///
/// Use for endpoints where the upstream spec defines `success` rather than `status`.
#[derive(Debug, Serialize, Deserialize)]
pub struct SuccessResponse {
    pub success: bool,
}

/// `{ success: bool, message: String }` response (e.g. delete-user).
///
/// Use for endpoints where the upstream spec defines `success` rather than `status`.
#[derive(Debug, Serialize, Deserialize)]
pub struct SuccessMessageResponse {
    pub success: bool,
    pub message: String,
}

/// Health-check response for `/health`.
#[derive(Debug, Serialize)]
pub struct HealthCheckResponse {
    pub status: &'static str,
    pub service: &'static str,
}

/// Error body `{ message, version, traceId }`.
#[derive(Debug, Serialize)]
pub struct ErrorMessageResponse {
    pub message: String,
    pub version: String,
    #[serde(rename = "traceId")]
    pub trace_id: String,
}

impl ErrorMessageResponse {
    pub fn new(
        message: impl Into<String>,
        version: impl Into<String>,
        trace_id: impl Into<String>,
    ) -> Self {
        Self {
            message: message.into(),
            version: version.into(),
            trace_id: trace_id.into(),
        }
    }

    pub fn with_generated_trace_id(message: impl Into<String>, version: impl Into<String>) -> Self {
        Self::new(message, version, uuid::Uuid::new_v4().to_string())
    }
}

/// Middleware error response `{ code: String, message: String }`.
#[derive(Debug, Serialize)]
pub struct CodeMessageResponse {
    pub code: &'static str,
    pub message: String,
}

/// Rate-limit error response with `retryAfter` field.
#[derive(Debug, Serialize)]
pub struct RateLimitErrorResponse {
    pub code: &'static str,
    pub message: &'static str,
    #[serde(rename = "retryAfter")]
    pub retry_after: u64,
}

/// Validation error response `{ code, message, errors }`.
#[derive(Debug, Serialize)]
pub struct ValidationErrorResponse<'a> {
    pub code: &'static str,
    pub message: &'static str,
    pub errors: std::collections::HashMap<&'a str, Vec<String>>,
}

/// Parameters for listing users (admin endpoint).
#[derive(Debug, Clone, Default)]
pub struct ListUsersParams {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub search_field: Option<String>,
    pub search_value: Option<String>,
    pub search_operator: Option<String>,
    pub sort_by: Option<String>,
    pub sort_direction: Option<String>,
    pub filter_field: Option<String>,
    pub filter_value: Option<String>,
    pub filter_operator: Option<String>,
}
