porta-rs 0.1.1

Zero-trust CGNAT bypass via WireGuard tunnel
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use std::fs;
use std::path::PathBuf;

use super::collector::MetricsSnapshot;

/// Metrics storage
pub struct MetricsStorage {
    path: PathBuf,
}

impl MetricsStorage {
    pub fn new(data_dir: &str) -> Self {
        Self {
            path: PathBuf::from(data_dir).join("metrics.json"),
        }
    }

    /// Load metrics from disk
    pub fn load(&self) -> Result<Vec<MetricsSnapshot>> {
        if !self.path.exists() {
            return Ok(Vec::new());
        }

        let content = fs::read_to_string(&self.path)
            .context(format!("Failed to read metrics from {}", self.path.display()))?;

        let snapshots: Vec<MetricsSnapshot> = serde_json::from_str(&content)
            .context("Failed to parse metrics")?;

        Ok(snapshots)
    }

    /// Save metrics to disk
    pub fn save(&self, snapshots: &[MetricsSnapshot]) -> Result<()> {
        let content = serde_json::to_string_pretty(snapshots)
            .context("Failed to serialize metrics")?;

        fs::write(&self.path, content)
            .context(format!("Failed to write metrics to {}", self.path.display()))?;

        Ok(())
    }

    /// Append a snapshot and save
    pub fn append(&self, snapshot: &MetricsSnapshot) -> Result<()> {
        let mut snapshots = self.load()?;
        snapshots.push(snapshot.clone());

        // Keep only last 1000 snapshots
        if snapshots.len() > 1000 {
            let drain_count = snapshots.len() - 1000;
            snapshots.drain(..drain_count);
        }

        self.save(&snapshots)
    }

    /// Get metrics for a time range
    pub fn get_range(&self, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<MetricsSnapshot>> {
        let snapshots = self.load()?;

        Ok(snapshots
            .into_iter()
            .filter(|s| s.timestamp >= start && s.timestamp <= end)
            .collect())
    }
}