use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
use chrono::{DateTime, Utc};
use validator::Validate;

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct Tenant {
    pub id: Uuid,
    pub parent_id: Option<Uuid>,
    pub name: String,
    pub slug: String,
    pub settings: serde_json::Value,
    pub tier: String,
    pub status: String,
    pub usage_limits: serde_json::Value,
    pub billing_id: Option<String>,
    pub trial_expires_at: Option<DateTime<Utc>>,
    pub created_at: DateTime<Utc>,
    #[serde(default)]
    pub stripe_subscription_id: Option<String>,
    #[serde(default)]
    pub stripe_metered_item_id: Option<String>,
    #[serde(default = "default_billing_status")]
    pub billing_status: String,
}

fn default_billing_status() -> String {
    "none".to_string()
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, Validate, utoipa::ToSchema)]
pub struct DataSource {
    pub id: Uuid,
    pub tenant_id: Uuid,
    #[validate(length(min = 1, max = 100))]
    pub name: String,
    #[validate(length(min = 1, max = 50))]
    pub source_type: String,
    pub config: serde_json::Value,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct ExternalEntity {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub data_source_id: Uuid,
    pub external_id: String,
    pub entity_type: String,
    pub raw_data: serde_json::Value,
    pub normalized_data: Option<serde_json::Value>,
    pub ingested_at: DateTime<Utc>,
    pub batch_id: Option<Uuid>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct CanonicalEntity {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub entity_type: String,
    pub canonical_data: serde_json::Value,
    pub field_provenance: serde_json::Value,
    pub confidence_score: f64,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub is_locked: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct IdentityLink {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub external_entity_id: Uuid,
    pub canonical_entity_id: Uuid,
    pub link_type: String,
    pub confidence: f64,
    pub matched_on: serde_json::Value,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct MatchResult {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub batch_id: Uuid,
    pub entity_a_id: Uuid,
    pub entity_b_id: Uuid,
    pub decision: String,
    pub confidence: f64,
    pub rules_applied: serde_json::Value,
    pub explanation: serde_json::Value,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct MatchAudit {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub batch_run_id: Option<Uuid>,
    pub entity_a_id: Uuid,
    pub entity_b_id: Uuid,
    pub decision: String,
    pub confidence: f64,
    pub rule_trace: serde_json::Value,
    pub blocking_key: Option<String>,
    pub temporal_adjustment: Option<f64>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, Validate, utoipa::ToSchema)]
pub struct MatchRule {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub version: i32,
    #[validate(length(min = 1, max = 100))]
    pub name: String,
    #[validate(length(min = 1, max = 50))]
    pub rule_type: String,
    pub config: serde_json::Value,
    #[validate(range(min = 0.0, max = 1.0))]
    pub weight: f64,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub created_by: Option<Uuid>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, Validate, utoipa::ToSchema)]
pub struct ManualOverride {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub canonical_entity_id: Option<Uuid>,
    #[validate(length(min = 1, max = 50))]
    pub override_type: String,
    pub override_data: serde_json::Value,
    #[validate(length(max = 500))]
    pub reason: Option<String>,
    pub created_by: Option<Uuid>,
    pub created_at: DateTime<Utc>,
    pub superseded_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct BatchRun {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub status: String,
    pub job_type: String,
    pub stats: serde_json::Value,
    pub started_at: Option<DateTime<Utc>>,
    pub completed_at: Option<DateTime<Utc>>,
    pub triggered_by: Option<Uuid>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct AuditEvent {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub actor_id: Uuid,
    pub actor_type: String,
    pub action: String,
    pub resource_type: String,
    pub resource_id: Uuid,
    pub before_state: Option<serde_json::Value>,
    pub after_state: Option<serde_json::Value>,
    pub reason: Option<String>,
    pub timestamp: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, Validate, utoipa::ToSchema)]
pub struct User {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub external_id: Option<String>,
    pub provisioning_source: Option<String>,
    #[validate(email)]
    pub email: String,
    #[validate(length(min = 1, max = 100))]
    pub name: String,
    #[validate(length(min = 1, max = 50))]
    pub role: String,
    pub hashed_password: Option<String>,
    pub must_change_password: bool,
    #[serde(default)]
    pub is_verified: bool,
    pub verification_token: Option<String>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct UserResponse {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub external_id: Option<String>,
    pub provisioning_source: Option<String>,
    pub email: String,
    pub name: String,
    pub role: String,
    pub must_change_password: bool,
    pub is_verified: bool,
    pub created_at: DateTime<Utc>,
}

impl From<User> for UserResponse {
    fn from(user: User) -> Self {
        Self {
            id: user.id,
            tenant_id: user.tenant_id,
            external_id: user.external_id,
            provisioning_source: user.provisioning_source,
            email: user.email,
            name: user.name,
            role: user.role,
            must_change_password: user.must_change_password,
            is_verified: user.is_verified,
            created_at: user.created_at,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct ApiKey {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub name: String,
    pub key_hash: String,
    pub key_prefix: String,
    pub scopes: serde_json::Value,
    pub expires_at: Option<DateTime<Utc>>,
    pub last_used_at: Option<DateTime<Utc>>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct RefreshToken {
    pub id: Uuid,
    pub token_hash: String,
    pub user_id: Uuid,
    pub tenant_id: Uuid,
    pub family_id: Uuid,
    pub parent_id: Option<Uuid>,
    pub revoked_at: Option<DateTime<Utc>>,
    pub replaced_at: Option<DateTime<Utc>>,
    pub expires_at: DateTime<Utc>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct SsoConfig {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub provider_type: String,
    pub display_name: String,
    pub issuer_url: String,
    pub client_id: Option<String>,
    pub client_secret_enc: Option<String>,
    pub auth_url: Option<String>,
    pub token_url: Option<String>,
    pub jwks_url: Option<String>,
    pub saml_metadata_url: Option<String>,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct AuditStream {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub destination_type: String,
    pub destination_url: String,
    pub auth_config: serde_json::Value,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct RetentionPolicy {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub entity_type: String,
    pub retention_days: i32,
    pub shred_pii: bool,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct SurvivorshipPolicy {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub field_name: String,
    pub priority_config: serde_json::Value,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct FieldPriority {
    pub source: String,
    pub priority: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaConfig {
    pub selected_columns: Option<Vec<String>>,
    pub sql_transformation: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct SecuritySettings {
    pub hmac_key: String, // Hex encoded
    pub encryption_key: Option<String>, // Hex encoded local key
    pub kms_provider: Option<String>, // "aws", "gcp", "vault"
    pub kms_key_uri: Option<String>,
    pub hashed_fields: Vec<String>,
    pub encrypted_fields: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct ReconciliationSettings {
    pub merge_threshold: f64,
    pub review_threshold: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct IdentityPlanModel {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub identity_version: String,
    pub plan_hash: String,
    pub raw_yaml: String,
    pub compiled_plan: serde_json::Value,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, FromRow, utoipa::ToSchema)]
pub struct SchemaMapping {
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub data_source_id: Uuid,
    pub mapping_config: serde_json::Value,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

// ============================================================================
// Tenant Tier
// ============================================================================

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TenantTier {
    Starter,
    Growth,
    Enterprise,
}

impl<'de> serde::Deserialize<'de> for TenantTier {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        match s.as_str() {
            "starter" => Ok(TenantTier::Starter),
            "growth" => Ok(TenantTier::Growth),
            "cloud" => Ok(TenantTier::Growth), // backward compat
            "enterprise" => Ok(TenantTier::Enterprise),
            other => Err(serde::de::Error::unknown_variant(
                other,
                &["starter", "growth", "cloud", "enterprise"],
            )),
        }
    }
}

impl Serialize for TenantTier {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            TenantTier::Starter => serializer.serialize_str("starter"),
            TenantTier::Growth => serializer.serialize_str("growth"),
            TenantTier::Enterprise => serializer.serialize_str("enterprise"),
        }
    }
}

impl TenantTier {
    pub fn from_tier_str(s: &str) -> Self {
        match s {
            "enterprise" => Self::Enterprise,
            "growth" | "cloud" | "business" => Self::Growth,
            _ => Self::Starter, // "free", "starter", "basic", unknown → Starter
        }
    }

    pub fn has_exclusions(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_advanced_scoring(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_compliance(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_freshness(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_hierarchy(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_audit_config(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_metadata(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_sampling(&self) -> bool { matches!(self, Self::Growth | Self::Enterprise) }
    pub fn has_relations(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_governance(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_reference_identifiers(&self) -> bool { matches!(self, Self::Growth | Self::Enterprise) }
    pub fn has_sso(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_scim(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_siem(&self) -> bool { matches!(self, Self::Enterprise) }
    pub fn has_retention(&self) -> bool { matches!(self, Self::Enterprise) }
}

impl std::fmt::Display for TenantTier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Starter => write!(f, "starter"),
            Self::Growth => write!(f, "growth"),
            Self::Enterprise => write!(f, "enterprise"),
        }
    }
}

// Newtype wrappers for type safety
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct TenantId(pub Uuid);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct UserId(pub Uuid);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct EntityId(pub Uuid);

impl From<Uuid> for TenantId { fn from(id: Uuid) -> Self { Self(id) } }
impl From<Uuid> for UserId { fn from(id: Uuid) -> Self { Self(id) } }
impl From<Uuid> for EntityId { fn from(id: Uuid) -> Self { Self(id) } }
impl From<TenantId> for Uuid { fn from(id: TenantId) -> Self { id.0 } }
impl From<UserId> for Uuid { fn from(id: UserId) -> Self { id.0 } }
impl From<EntityId> for Uuid { fn from(id: EntityId) -> Self { id.0 } }

// Pagination
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaginationParams {
    pub limit: Option<i64>,
    pub offset: Option<i64>,
}

impl PaginationParams {
    pub fn limit(&self) -> i64 { self.limit.unwrap_or(50).min(1000) }
    pub fn offset(&self) -> i64 { self.offset.unwrap_or(0) }
}

// Standard API response envelope
#[derive(Debug, Serialize)]
pub struct ApiResponse<T: Serialize> {
    pub data: T,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<PaginationMeta>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PaginationMeta {
    pub total: i64,
    pub limit: i64,
    pub offset: i64,
}

impl<T: Serialize> ApiResponse<T> {
    pub fn new(data: T) -> Self { Self { data, meta: None } }
    pub fn with_pagination(data: T, total: i64, limit: i64, offset: i64) -> Self {
        Self { data, meta: Some(PaginationMeta { total, limit, offset }) }
    }
}

pub mod crypto {
    use ring::{hmac, aead};
    use anyhow::{anyhow, Result};

    pub fn hmac_sha256(key: &[u8], data: &[u8]) -> String {
        let s_key = hmac::Key::new(hmac::HMAC_SHA256, key);
        let tag = hmac::sign(&s_key, data);
        hex::encode(tag.as_ref())
    }

    pub fn encrypt_gcm(key: &[u8], nonce: &[u8], data: &[u8]) -> Result<Vec<u8>> {
        let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, key)
            .map_err(|_| anyhow!("Failed to create AEAD key"))?;
        let sealing_key = aead::LessSafeKey::new(unbound_key);
        let nonce = aead::Nonce::try_assume_unique_for_key(nonce)
            .map_err(|_| anyhow!("Invalid nonce"))?;
        
        let mut in_out = data.to_vec();
        sealing_key.seal_in_place_append_tag(nonce, aead::Aad::empty(), &mut in_out)
            .map_err(|_| anyhow!("Encryption failed"))?;
        
        Ok(in_out)
    }

    pub fn decrypt_gcm(key: &[u8], nonce: &[u8], ciphertext: &mut [u8]) -> Result<Vec<u8>> {
        let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, key)
            .map_err(|_| anyhow!("Failed to create AEAD key"))?;
        let opening_key = aead::LessSafeKey::new(unbound_key);
        let nonce = aead::Nonce::try_assume_unique_for_key(nonce)
            .map_err(|_| anyhow!("Invalid nonce"))?;
        
        let decrypted_data = opening_key.open_in_place(nonce, aead::Aad::empty(), ciphertext)
            .map_err(|_| anyhow!("Decryption failed"))?;
        
        Ok(decrypted_data.to_vec())
    }

    pub fn encrypt_gcm_with_nonce(key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
        use ring::rand::{SystemRandom, SecureRandom};
        let rand = SystemRandom::new();
        let mut nonce_bytes = [0u8; 12];
        rand.fill(&mut nonce_bytes).map_err(|_| anyhow!("Failed to generate nonce"))?;
        
        let encrypted = encrypt_gcm(key, &nonce_bytes, data)?;
        
        let mut combined = nonce_bytes.to_vec();
        combined.extend(encrypted);
        Ok(combined)
    }

    pub fn decrypt_gcm_with_nonce(key: &[u8], combined: &[u8]) -> Result<Vec<u8>> {
        if combined.len() < 12 {
            return Err(anyhow!("Invalid ciphertext"));
        }
        let (nonce, ciphertext) = combined.split_at(12);
        let mut ciphertext_vec = ciphertext.to_vec();
        decrypt_gcm(key, nonce, &mut ciphertext_vec)
    }
}

#[cfg(test)]
mod tests {
    use super::crypto::*;

    #[test]
    fn test_hmac_sha256() {
        let key = b"secret-key";
        let data = b"hello world";
        let hash = hmac_sha256(key, data);
        assert!(!hash.is_empty());
        
        let hash2 = hmac_sha256(key, data);
        assert_eq!(hash, hash2);
    }

    #[test]
    fn test_encrypt_decrypt_gcm_with_nonce() {
        let key = vec![0u8; 32];
        let data = b"sensitive data";
        
        let combined = encrypt_gcm_with_nonce(&key, data).unwrap();
        assert!(combined.len() > 12);
        
        let decrypted = decrypt_gcm_with_nonce(&key, &combined).unwrap();
        assert_eq!(decrypted, data);
    }
}
