use crate::db::QueryResult;
use crate::error::Result;
use crate::event::Event;
use crate::nip05::VerificationRecord;
use crate::payment::{InvoiceInfo, InvoiceStatus};
use crate::subscription::Subscription;
use crate::utils::unix_time;
use async_trait::async_trait;
use nostr::Keys;
use rand::Rng;

pub mod postgres;
pub mod postgres_migration;
pub mod sqlite;
pub mod sqlite_migration;

#[async_trait]
pub trait NostrRepo: Send + Sync {
    /// Start the repository (any initialization or maintenance tasks can be kicked off here)
    async fn start(&self) -> Result<()>;

    /// Run migrations and return current version
    async fn migrate_up(&self) -> Result<usize>;

    /// Persist event to database
    async fn write_event(&self, e: &Event) -> Result<u64>;

    /// Perform a database query using a subscription.
    ///
    /// The [`Subscription`] is converted into a SQL query.  Each result
    /// is published on the `query_tx` channel as it is returned.  If a
    /// message becomes available on the `abandon_query_rx` channel, the
    /// query is immediately aborted.
    async fn query_subscription(
        &self,
        sub: Subscription,
        client_id: String,
        query_tx: tokio::sync::mpsc::Sender<QueryResult>,
        mut abandon_query_rx: tokio::sync::oneshot::Receiver<()>,
    ) -> Result<()>;

    /// Perform normal maintenance
    async fn optimize_db(&self) -> Result<()>;

    /// Create a new verification record connected to a specific event
    async fn create_verification_record(&self, event_id: &str, name: &str) -> Result<()>;

    /// Update verification timestamp
    async fn update_verification_timestamp(&self, id: u64) -> Result<()>;

    /// Update verification record as failed
    async fn fail_verification(&self, id: u64) -> Result<()>;

    /// Delete verification record
    async fn delete_verification(&self, id: u64) -> Result<()>;

    /// Get the latest verification record for a given pubkey.
    async fn get_latest_user_verification(&self, pub_key: &str) -> Result<VerificationRecord>;

    /// Get oldest verification before timestamp
    async fn get_oldest_user_verification(&self, before: u64) -> Result<VerificationRecord>;

    /// Create a new account
    async fn create_account(&self, pubkey: &Keys) -> Result<bool>;

    /// Admit an account
    async fn admit_account(&self, pubkey: &Keys, admission_cost: u64) -> Result<()>;

    /// Gets user balance if they are an admitted pubkey
    async fn get_account_balance(&self, pubkey: &Keys) -> Result<(bool, u64)>;

    /// Update account balance
    async fn update_account_balance(
        &self,
        pub_key: &Keys,
        positive: bool,
        new_balance: u64,
    ) -> Result<()>;

    /// Create invoice record
    async fn create_invoice_record(&self, pubkey: &Keys, invoice_info: InvoiceInfo) -> Result<()>;

    /// Update Invoice for given payment hash
    async fn update_invoice(&self, payment_hash: &str, status: InvoiceStatus) -> Result<String>;

    /// Get the most recent invoice for a given pubkey
    /// invoice must be unpaid and not expired
    async fn get_unpaid_invoice(&self, pubkey: &Keys) -> Result<Option<InvoiceInfo>>;
}

// Current time, with a slight forward jitter in seconds
pub(crate) fn now_jitter(sec: u64) -> u64 {
    // random time between now, and 10min in future.
    let mut rng = rand::thread_rng();
    let jitter_amount = rng.gen_range(0..sec);
    let now = unix_time();
    now.saturating_add(jitter_amount)
}