//! Certificate management — load, save, check expiry, generate renewal warnings.

use crate::{PublishError, PublishResult};
use std::time::{SystemTime, UNIX_EPOCH};

/// Info about a code signing certificate.
#[derive(Debug, Clone)]
pub struct CertInfo {
    pub name: String,
    pub team_id: String,
    pub serial: String,
    /// Certificate type: "Distribution", "Development", "Push", etc.
    pub cert_type: String,
    /// Expiry as Unix timestamp (seconds since epoch).
    pub expires_at: u64,
    /// Whether the certificate is currently valid.
    pub is_valid: bool,
}

impl CertInfo {
    pub fn new(
        name: impl Into<String>,
        team_id: impl Into<String>,
        serial: impl Into<String>,
        cert_type: impl Into<String>,
        expires_at: u64,
    ) -> Self {
        let now = unix_now();
        Self {
            name: name.into(),
            team_id: team_id.into(),
            serial: serial.into(),
            cert_type: cert_type.into(),
            expires_at,
            is_valid: expires_at > now,
        }
    }

    /// Days until expiry (0 if already expired).
    pub fn days_until_expiry(&self) -> u64 {
        let now = unix_now();
        if self.expires_at <= now {
            return 0;
        }
        (self.expires_at - now) / 86400
    }

    /// Whether the cert expires within `days` days.
    pub fn expires_soon(&self, days: u64) -> bool {
        self.days_until_expiry() <= days
    }

    pub fn is_expired(&self) -> bool {
        unix_now() >= self.expires_at
    }
}

/// Certificate store — loads, caches, and checks expiry of code signing certs.
pub struct CertStore {
    certs: Vec<CertInfo>,
    /// How many days before expiry to warn (default: 30).
    pub warn_days: u64,
}

impl CertStore {
    pub fn new() -> Self {
        Self { certs: Vec::new(), warn_days: 30 }
    }

    pub fn with_warn_days(mut self, days: u64) -> Self {
        self.warn_days = days;
        self
    }

    /// Add a certificate to the store.
    pub fn add(&mut self, cert: CertInfo) {
        self.certs.push(cert);
    }

    /// Find a certificate by team ID and type.
    pub fn find(&self, team_id: &str, cert_type: &str) -> Option<&CertInfo> {
        self.certs.iter().find(|c| {
            c.team_id == team_id && c.cert_type == cert_type && !c.is_expired()
        })
    }

    /// Get all certificates expiring soon (within `warn_days`).
    pub fn expiring_soon(&self) -> Vec<&CertInfo> {
        self.certs
            .iter()
            .filter(|c| c.expires_soon(self.warn_days))
            .collect()
    }

    /// Generate renewal warnings for expiring certificates.
    pub fn renewal_warnings(&self) -> Vec<String> {
        self.expiring_soon()
            .iter()
            .map(|c| {
                if c.is_expired() {
                    format!(
                        "EXPIRED: {} ({}) — team {}. Renew immediately.",
                        c.name, c.cert_type, c.team_id
                    )
                } else {
                    format!(
                        "EXPIRING SOON: {} ({}) expires in {} days — team {}. Renew before publishing.",
                        c.name, c.cert_type, c.days_until_expiry(), c.team_id
                    )
                }
            })
            .collect()
    }

    /// Check that a valid distribution certificate exists for the given team.
    pub fn validate_for_distribution(&self, team_id: &str) -> PublishResult<()> {
        let cert = self.find(team_id, "Distribution");
        match cert {
            None => Err(PublishError::Certificate(format!(
                "no valid Distribution certificate found for team {}. Run: el auth add-apple",
                team_id
            ))),
            Some(c) if c.expires_soon(7) => Err(PublishError::Certificate(format!(
                "Distribution certificate for team {} expires in {} days. Renew now.",
                team_id,
                c.days_until_expiry()
            ))),
            _ => Ok(()),
        }
    }

    /// Load certificates from a JSON file (stub — real impl would parse
    /// Apple's certificate PEM files or keychain API).
    pub fn load_from_file(_path: &str) -> PublishResult<Self> {
        // TODO: parse certificate PEM/P12 files, extract expiry via x509-parser
        Ok(Self::new())
    }

    /// Save certificate metadata to a JSON cache file.
    pub fn save_to_file(&self, _path: &str) -> PublishResult<()> {
        // TODO: serialize cert metadata to JSON
        Ok(())
    }
}

impl Default for CertStore {
    fn default() -> Self {
        Self::new()
    }
}

fn unix_now() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0)
}
