//! Shared audit/event lifecycle classification.
//!
//! This module gives `tsafe-core` one place to answer:
//! - what broad kind of operation happened
//! - whether that operation belongs to an explicit lifecycle state
//! - which CloudEvents type should be emitted when the operation is well-known
//!
//! The goal is to keep audit explanation and event projection aligned instead of
//! carrying multiple hard-coded operation taxonomies.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OperationKind {
    SecretLifecycle,
    VaultLifecycle,
    Execution,
    Session,
    Sync,
    Share,
    Team,
    RotationPolicy,
    CredentialHelper,
    Other,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VaultLifecycleState {
    Created,
    PasswordRotated,
    SnapshotRestored,
    SecretMoved,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SecretLifecycleState {
    Written,
    Accessed,
    Deleted,
    Imported,
    Exported,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ShareLifecycleState {
    Published,
    Consumed,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TeamLifecycleState {
    MemberAdded,
    MemberRemoved,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PolicyLifecycleState {
    Set,
    Removed,
    DueChecked,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SessionLifecycleState {
    Unlocked,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SyncLifecycleState {
    PullCompleted,
    Merged,
    TeamKeysSynced,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CredentialHelperLifecycleState {
    Accessed,
    Stored,
    Erased,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "domain", content = "state", rename_all = "snake_case")]
pub enum LifecycleState {
    Secret(SecretLifecycleState),
    Vault(VaultLifecycleState),
    Share(ShareLifecycleState),
    Team(TeamLifecycleState),
    Policy(PolicyLifecycleState),
    Session(SessionLifecycleState),
    Sync(SyncLifecycleState),
    CredentialHelper(CredentialHelperLifecycleState),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OperationClassification {
    pub kind: OperationKind,
    pub lifecycle_state: Option<LifecycleState>,
    pub event_type: Option<&'static str>,
}

pub fn classify_operation(operation: &str) -> OperationClassification {
    match operation {
        "set" => secret(
            SecretLifecycleState::Written,
            Some("com.tsafe.vault.secret.set.v1"),
        ),
        "delete" => secret(
            SecretLifecycleState::Deleted,
            Some("com.tsafe.vault.secret.deleted.v1"),
        ),
        "get" | "browser-get" | "browser-list" => secret(
            SecretLifecycleState::Accessed,
            Some("com.tsafe.vault.secret.accessed.v1"),
        ),
        "alias" | "gen" | "pin" | "unpin" | "ssh-add" | "ssh-import" | "totp-add"
        | "browser-save" => secret(SecretLifecycleState::Written, None),
        "totp-get" | "qr" => secret(SecretLifecycleState::Accessed, None),
        "import" => secret(
            SecretLifecycleState::Imported,
            Some("com.tsafe.vault.imported.v1"),
        ),
        "export" => secret(
            SecretLifecycleState::Exported,
            Some("com.tsafe.vault.exported.v1"),
        ),

        "init" | "create" | "team-init" => vault(
            VaultLifecycleState::Created,
            Some("com.tsafe.vault.created.v1"),
        ),
        "rotate" => vault(
            VaultLifecycleState::PasswordRotated,
            Some("com.tsafe.vault.rotated.v1"),
        ),
        "snapshot-restore" => vault(VaultLifecycleState::SnapshotRestored, None),
        "mv" | "ns-move" => vault(VaultLifecycleState::SecretMoved, None),
        "ns-copy" => secret(SecretLifecycleState::Written, None),

        "exec" | "plugin" => OperationClassification {
            kind: OperationKind::Execution,
            lifecycle_state: None,
            event_type: Some("com.tsafe.vault.exec.v1"),
        },
        "unlock" => session(
            SessionLifecycleState::Unlocked,
            Some("com.tsafe.session.unlocked.v1"),
        ),
        "kv-pull" | "vault-pull" | "op-pull" | "pull" => sync(
            SyncLifecycleState::PullCompleted,
            Some("com.tsafe.sync.pull.completed.v1"),
        ),
        "sync" => sync(SyncLifecycleState::Merged, None),
        "team-sync-keys" => sync(SyncLifecycleState::TeamKeysSynced, None),
        "team-add-member" => team(TeamLifecycleState::MemberAdded),
        "team-remove-member" => team(TeamLifecycleState::MemberRemoved),
        "share-once" | "snap" => share(
            ShareLifecycleState::Published,
            Some("com.tsafe.share.published.v1"),
        ),
        "receive-once" | "snap-receive" => share(
            ShareLifecycleState::Consumed,
            Some("com.tsafe.share.consumed.v1"),
        ),
        "policy-set" => policy(PolicyLifecycleState::Set, None),
        "policy-remove" => policy(PolicyLifecycleState::Removed, None),
        "doctor" => OperationClassification {
            kind: OperationKind::RotationPolicy,
            lifecycle_state: None,
            event_type: None,
        },
        "rotate-due" => policy(
            PolicyLifecycleState::DueChecked,
            Some("com.tsafe.secret.rotation_due.v1"),
        ),
        "credential-helper-get" => helper(CredentialHelperLifecycleState::Accessed),
        "credential-helper-store" => helper(CredentialHelperLifecycleState::Stored),
        "credential-helper-erase" => helper(CredentialHelperLifecycleState::Erased),
        _ => OperationClassification {
            kind: OperationKind::Other,
            lifecycle_state: None,
            event_type: None,
        },
    }
}

fn secret(
    state: SecretLifecycleState,
    event_type: Option<&'static str>,
) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::SecretLifecycle,
        lifecycle_state: Some(LifecycleState::Secret(state)),
        event_type,
    }
}

fn vault(state: VaultLifecycleState, event_type: Option<&'static str>) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::VaultLifecycle,
        lifecycle_state: Some(LifecycleState::Vault(state)),
        event_type,
    }
}

fn share(state: ShareLifecycleState, event_type: Option<&'static str>) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::Share,
        lifecycle_state: Some(LifecycleState::Share(state)),
        event_type,
    }
}

fn policy(
    state: PolicyLifecycleState,
    event_type: Option<&'static str>,
) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::RotationPolicy,
        lifecycle_state: Some(LifecycleState::Policy(state)),
        event_type,
    }
}

fn team(state: TeamLifecycleState) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::Team,
        lifecycle_state: Some(LifecycleState::Team(state)),
        event_type: None,
    }
}

fn session(
    state: SessionLifecycleState,
    event_type: Option<&'static str>,
) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::Session,
        lifecycle_state: Some(LifecycleState::Session(state)),
        event_type,
    }
}

fn sync(state: SyncLifecycleState, event_type: Option<&'static str>) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::Sync,
        lifecycle_state: Some(LifecycleState::Sync(state)),
        event_type,
    }
}

fn helper(state: CredentialHelperLifecycleState) -> OperationClassification {
    OperationClassification {
        kind: OperationKind::CredentialHelper,
        lifecycle_state: Some(LifecycleState::CredentialHelper(state)),
        event_type: None,
    }
}

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

    #[test]
    fn init_is_classified_as_created_vault_lifecycle() {
        let classified = classify_operation("init");
        assert_eq!(classified.kind, OperationKind::VaultLifecycle);
        assert_eq!(
            classified.lifecycle_state,
            Some(LifecycleState::Vault(VaultLifecycleState::Created))
        );
        assert_eq!(classified.event_type, Some("com.tsafe.vault.created.v1"));
    }

    #[test]
    fn create_and_team_init_reuse_created_vault_lifecycle() {
        let created = classify_operation("create");
        assert_eq!(created.kind, OperationKind::VaultLifecycle);
        assert_eq!(
            created.lifecycle_state,
            Some(LifecycleState::Vault(VaultLifecycleState::Created))
        );
        assert_eq!(created.event_type, Some("com.tsafe.vault.created.v1"));

        let team_created = classify_operation("team-init");
        assert_eq!(team_created.kind, OperationKind::VaultLifecycle);
        assert_eq!(
            team_created.lifecycle_state,
            Some(LifecycleState::Vault(VaultLifecycleState::Created))
        );
        assert_eq!(team_created.event_type, Some("com.tsafe.vault.created.v1"));
    }

    #[test]
    fn share_once_is_classified_as_published_share() {
        let classified = classify_operation("share-once");
        assert_eq!(classified.kind, OperationKind::Share);
        assert_eq!(
            classified.lifecycle_state,
            Some(LifecycleState::Share(ShareLifecycleState::Published))
        );
        assert_eq!(classified.event_type, Some("com.tsafe.share.published.v1"));
    }

    #[test]
    fn snapshot_restore_keeps_explicit_vault_state_without_forcing_new_event_type() {
        let classified = classify_operation("snapshot-restore");
        assert_eq!(classified.kind, OperationKind::VaultLifecycle);
        assert_eq!(
            classified.lifecycle_state,
            Some(LifecycleState::Vault(VaultLifecycleState::SnapshotRestored))
        );
        assert_eq!(classified.event_type, None);
    }

    #[test]
    fn unlock_is_classified_as_unlocked_session() {
        let classified = classify_operation("unlock");
        assert_eq!(classified.kind, OperationKind::Session);
        assert_eq!(
            classified.lifecycle_state,
            Some(LifecycleState::Session(SessionLifecycleState::Unlocked))
        );
        assert_eq!(classified.event_type, Some("com.tsafe.session.unlocked.v1"));
    }

    #[test]
    fn pull_and_sync_operations_have_explicit_sync_states() {
        let pull = classify_operation("kv-pull");
        assert_eq!(pull.kind, OperationKind::Sync);
        assert_eq!(
            pull.lifecycle_state,
            Some(LifecycleState::Sync(SyncLifecycleState::PullCompleted))
        );
        assert_eq!(pull.event_type, Some("com.tsafe.sync.pull.completed.v1"));

        let merged = classify_operation("sync");
        assert_eq!(
            merged.lifecycle_state,
            Some(LifecycleState::Sync(SyncLifecycleState::Merged))
        );
        assert_eq!(merged.event_type, None);

        let team = classify_operation("team-sync-keys");
        assert_eq!(
            team.lifecycle_state,
            Some(LifecycleState::Sync(SyncLifecycleState::TeamKeysSynced))
        );
        assert_eq!(team.event_type, None);
    }

    #[test]
    fn secret_operations_have_explicit_secret_states() {
        let set = classify_operation("set");
        assert_eq!(set.kind, OperationKind::SecretLifecycle);
        assert_eq!(
            set.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Written))
        );
        assert_eq!(set.event_type, Some("com.tsafe.vault.secret.set.v1"));

        let get = classify_operation("get");
        assert_eq!(
            get.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Accessed))
        );
        assert_eq!(get.event_type, Some("com.tsafe.vault.secret.accessed.v1"));

        let delete = classify_operation("delete");
        assert_eq!(
            delete.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Deleted))
        );
        assert_eq!(delete.event_type, Some("com.tsafe.vault.secret.deleted.v1"));

        let imported = classify_operation("import");
        assert_eq!(
            imported.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Imported))
        );

        let exported = classify_operation("export");
        assert_eq!(
            exported.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Exported))
        );

        let generated = classify_operation("gen");
        assert_eq!(
            generated.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Written))
        );
        assert_eq!(generated.event_type, None);

        let qr = classify_operation("qr");
        assert_eq!(
            qr.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Accessed))
        );
        assert_eq!(qr.event_type, None);

        let namespace_copy = classify_operation("ns-copy");
        assert_eq!(
            namespace_copy.lifecycle_state,
            Some(LifecycleState::Secret(SecretLifecycleState::Written))
        );
        assert_eq!(namespace_copy.event_type, None);
    }

    #[test]
    fn namespace_move_reuses_secret_moved_vault_state() {
        let namespace_move = classify_operation("ns-move");
        assert_eq!(namespace_move.kind, OperationKind::VaultLifecycle);
        assert_eq!(
            namespace_move.lifecycle_state,
            Some(LifecycleState::Vault(VaultLifecycleState::SecretMoved))
        );
        assert_eq!(namespace_move.event_type, None);
    }

    #[test]
    fn policy_operations_have_explicit_policy_states() {
        let set = classify_operation("policy-set");
        assert_eq!(set.kind, OperationKind::RotationPolicy);
        assert_eq!(
            set.lifecycle_state,
            Some(LifecycleState::Policy(PolicyLifecycleState::Set))
        );
        assert_eq!(set.event_type, None);

        let removed = classify_operation("policy-remove");
        assert_eq!(
            removed.lifecycle_state,
            Some(LifecycleState::Policy(PolicyLifecycleState::Removed))
        );
        assert_eq!(removed.event_type, None);

        let due = classify_operation("rotate-due");
        assert_eq!(
            due.lifecycle_state,
            Some(LifecycleState::Policy(PolicyLifecycleState::DueChecked))
        );
        assert_eq!(due.event_type, Some("com.tsafe.secret.rotation_due.v1"));
    }

    #[test]
    fn credential_helper_operations_have_explicit_helper_states() {
        let get = classify_operation("credential-helper-get");
        assert_eq!(get.kind, OperationKind::CredentialHelper);
        assert_eq!(
            get.lifecycle_state,
            Some(LifecycleState::CredentialHelper(
                CredentialHelperLifecycleState::Accessed
            ))
        );
        assert_eq!(get.event_type, None);

        let store = classify_operation("credential-helper-store");
        assert_eq!(
            store.lifecycle_state,
            Some(LifecycleState::CredentialHelper(
                CredentialHelperLifecycleState::Stored
            ))
        );

        let erase = classify_operation("credential-helper-erase");
        assert_eq!(
            erase.lifecycle_state,
            Some(LifecycleState::CredentialHelper(
                CredentialHelperLifecycleState::Erased
            ))
        );
    }

    #[test]
    fn team_membership_operations_have_explicit_team_states() {
        let add = classify_operation("team-add-member");
        assert_eq!(add.kind, OperationKind::Team);
        assert_eq!(
            add.lifecycle_state,
            Some(LifecycleState::Team(TeamLifecycleState::MemberAdded))
        );
        assert_eq!(add.event_type, None);

        let remove = classify_operation("team-remove-member");
        assert_eq!(
            remove.lifecycle_state,
            Some(LifecycleState::Team(TeamLifecycleState::MemberRemoved))
        );
        assert_eq!(remove.event_type, None);
    }

    #[test]
    fn unknown_operation_falls_back_to_other() {
        let classified = classify_operation("custom-op");
        assert_eq!(classified.kind, OperationKind::Other);
        assert_eq!(classified.lifecycle_state, None);
        assert_eq!(classified.event_type, None);
    }
}
