use std::time::Duration;

use anyhow::Result;
use chainfire_client::Client;
use tokio::time::sleep;
use tracing::{info, warn};

pub struct ChainfireWatcher {
    endpoint: String,
    prefix: String,
    interval: Duration,
}

impl ChainfireWatcher {
    pub fn new(endpoint: String, prefix: String, interval_secs: u64) -> Self {
        Self {
            endpoint,
            prefix,
            interval: Duration::from_secs(interval_secs),
        }
    }

    pub async fn watch<F>(&self, mut callback: F) -> Result<()>
    where
        F: FnMut(Vec<(Vec<u8>, Vec<u8>)>) -> Result<()>,
    {
        let mut last_revision = 0u64;

        loop {
            match self.fetch_updates(last_revision).await {
                Ok((kvs, max_rev)) => {
                    if !kvs.is_empty() {
                        info!(
                            prefix = %self.prefix,
                            count = kvs.len(),
                            "detected changes in Chainfire"
                        );
                        if let Err(e) = callback(kvs) {
                            warn!(error = %e, "callback failed");
                        } else if max_rev > last_revision {
                            last_revision = max_rev;
                        }
                    } else if max_rev > last_revision {
                        last_revision = max_rev;
                    }
                }
                Err(e) => {
                    warn!(error = %e, "failed to fetch updates from Chainfire");
                }
            }

            sleep(self.interval).await;
        }
    }

    async fn fetch_updates(&self, last_revision: u64) -> Result<(Vec<(Vec<u8>, Vec<u8>)>, u64)> {
        let mut client = Client::connect(self.endpoint.clone()).await?;
        let (kvs, _) = client.scan_prefix(self.prefix.as_bytes(), 0).await?;

        // 簡易実装: 全てのKVペアを返す（revisionフィルタリングは未実装）
        let mut max_rev = last_revision;
        let mut result = Vec::new();
        for (k, v, rev) in kvs {
            if rev > last_revision {
                result.push((k, v));
                if rev > max_rev {
                    max_rev = rev;
                }
            }
        }

        Ok((result, max_rev))
    }
}
