//! SOC 2 audit trail for governance decisions.
//!
//! Provides append-only logging of all governance events and export
//! capabilities for compliance reporting.

use std::fs::{self, File, OpenOptions};
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::store::{governance_store_dir, GovernanceStore};
use super::types::ApprovalRequest;
use crate::AphoriaError;

/// Type of audit event.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuditEventType {
    /// Approval request created.
    RequestCreated,
    /// Stage was approved.
    StageApproved,
    /// Stage was rejected.
    StageRejected,
    /// Request was escalated.
    Escalated,
    /// Stage was auto-approved based on evidence.
    AutoApproved,
    /// Request expired due to timeout.
    Expired,
    /// Workflow completed (fully approved).
    WorkflowCompleted,
}

impl std::fmt::Display for AuditEventType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AuditEventType::RequestCreated => write!(f, "Request Created"),
            AuditEventType::StageApproved => write!(f, "Stage Approved"),
            AuditEventType::StageRejected => write!(f, "Stage Rejected"),
            AuditEventType::Escalated => write!(f, "Escalated"),
            AuditEventType::AutoApproved => write!(f, "Auto-Approved"),
            AuditEventType::Expired => write!(f, "Expired"),
            AuditEventType::WorkflowCompleted => write!(f, "Workflow Completed"),
        }
    }
}

/// An audit event record.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
    /// Unique event ID.
    pub id: Uuid,
    /// Type of event.
    pub event_type: AuditEventType,
    /// Request this event belongs to.
    pub request_id: Uuid,
    /// Pattern this event concerns.
    pub pattern_id: Uuid,
    /// Who performed the action.
    pub actor: String,
    /// When the event occurred.
    pub timestamp: DateTime<Utc>,
    /// Additional event details.
    pub details: serde_json::Value,
}

impl AuditEvent {
    /// Create a new audit event.
    pub fn new(
        event_type: AuditEventType,
        request_id: Uuid,
        pattern_id: Uuid,
        actor: String,
        details: serde_json::Value,
    ) -> Self {
        Self {
            id: Uuid::new_v4(),
            event_type,
            request_id,
            pattern_id,
            actor,
            timestamp: Utc::now(),
            details,
        }
    }
}

/// Export format for audit data.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExportFormat {
    /// JSON format.
    Json,
    /// CSV format.
    Csv,
    /// Markdown format.
    Markdown,
}

impl std::str::FromStr for ExportFormat {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "json" => Ok(ExportFormat::Json),
            "csv" => Ok(ExportFormat::Csv),
            "markdown" | "md" => Ok(ExportFormat::Markdown),
            _ => Err(format!("Unknown format '{}'. Use: json, csv, markdown", s)),
        }
    }
}

/// Audit trail for governance events.
pub struct AuditTrail {
    /// Directory for audit files.
    audit_dir: PathBuf,
}

impl AuditTrail {
    /// Create a new audit trail.
    pub fn new(audit_dir: impl AsRef<Path>) -> Result<Self, AphoriaError> {
        let audit_dir = audit_dir.as_ref().to_path_buf();
        fs::create_dir_all(&audit_dir).map_err(|e| {
            AphoriaError::Storage(format!("Failed to create audit directory: {}", e))
        })?;
        Ok(Self { audit_dir })
    }

    /// Open the default audit trail.
    pub fn open_default() -> Result<Self, AphoriaError> {
        let dir = governance_store_dir().join("audit");
        Self::new(dir)
    }

    /// Path to the audit events file.
    fn events_path(&self) -> PathBuf {
        self.audit_dir.join("events.jsonl")
    }

    /// Log an audit event.
    pub fn log_event(&self, event: AuditEvent) -> Result<(), AphoriaError> {
        let path = self.events_path();
        let mut file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(&path)
            .map_err(|e| AphoriaError::Storage(format!("Failed to open audit file: {}", e)))?;

        let json = serde_json::to_string(&event)
            .map_err(|e| AphoriaError::Storage(format!("Failed to serialize event: {}", e)))?;

        writeln!(file, "{}", json)
            .map_err(|e| AphoriaError::Storage(format!("Failed to write event: {}", e)))?;

        Ok(())
    }

    /// Get all audit events.
    ///
    /// Resilient parser that skips malformed lines rather than failing.
    pub fn get_all_events(&self) -> Result<Vec<AuditEvent>, AphoriaError> {
        let path = self.events_path();
        if !path.exists() {
            return Ok(Vec::new());
        }

        let file = File::open(&path)
            .map_err(|e| AphoriaError::Storage(format!("Failed to open audit file: {}", e)))?;
        let reader = BufReader::new(file);

        let mut events = Vec::new();
        let mut skipped = 0;

        for line in reader.lines() {
            let line = match line {
                Ok(l) => l,
                Err(_) => {
                    skipped += 1;
                    continue;
                }
            };

            if line.trim().is_empty() {
                continue;
            }

            // Try to parse each JSON object on the line (handles concatenated objects)
            let mut remaining = line.trim();
            while !remaining.is_empty() {
                // Find the end of the current JSON object
                match serde_json::from_str::<AuditEvent>(remaining) {
                    Ok(event) => {
                        events.push(event);
                        // Consumed the whole line
                        break;
                    }
                    Err(_) => {
                        // Try to find where the first JSON object ends by looking for }{
                        if let Some(split_pos) = remaining.find("}{") {
                            let first_obj = &remaining[..=split_pos];
                            if let Ok(event) = serde_json::from_str::<AuditEvent>(first_obj) {
                                events.push(event);
                            } else {
                                skipped += 1;
                            }
                            remaining = &remaining[split_pos + 1..];
                        } else {
                            // Can't parse, skip this line
                            skipped += 1;
                            break;
                        }
                    }
                }
            }
        }

        if skipped > 0 {
            tracing::warn!(skipped, "Skipped malformed audit entries");
        }

        Ok(events)
    }

    /// Get audit timeline for a specific pattern.
    pub fn get_pattern_timeline(&self, pattern_id: &Uuid) -> Result<Vec<AuditEvent>, AphoriaError> {
        let events = self.get_all_events()?;
        Ok(events.into_iter().filter(|e| e.pattern_id == *pattern_id).collect())
    }

    /// Get audit timeline for a specific request.
    pub fn get_request_timeline(&self, request_id: &Uuid) -> Result<Vec<AuditEvent>, AphoriaError> {
        let events = self.get_all_events()?;
        Ok(events.into_iter().filter(|e| e.request_id == *request_id).collect())
    }

    /// Export audit data to a file.
    pub fn export(&self, format: ExportFormat, output: &Path) -> Result<(), AphoriaError> {
        let events = self.get_all_events()?;

        // Also get request data for richer export
        let store = GovernanceStore::open_default()?;
        let requests = store.list_all()?;

        let content = match format {
            ExportFormat::Json => self.export_json(&events, &requests)?,
            ExportFormat::Csv => self.export_csv(&events)?,
            ExportFormat::Markdown => self.export_markdown(&events, &requests)?,
        };

        fs::write(output, content)
            .map_err(|e| AphoriaError::Storage(format!("Failed to write export: {}", e)))?;

        Ok(())
    }

    /// Export as JSON.
    fn export_json(
        &self,
        events: &[AuditEvent],
        requests: &[ApprovalRequest],
    ) -> Result<String, AphoriaError> {
        let export = serde_json::json!({
            "export_timestamp": Utc::now().to_rfc3339(),
            "events_count": events.len(),
            "requests_count": requests.len(),
            "events": events,
            "requests": requests,
        });

        serde_json::to_string_pretty(&export)
            .map_err(|e| AphoriaError::Storage(format!("Failed to serialize export: {}", e)))
    }

    /// Export as CSV.
    fn export_csv(&self, events: &[AuditEvent]) -> Result<String, AphoriaError> {
        let mut csv = String::new();
        csv.push_str("timestamp,event_type,request_id,pattern_id,actor,details\n");

        for event in events {
            let details_str =
                serde_json::to_string(&event.details).unwrap_or_else(|_| "{}".to_string());
            csv.push_str(&format!(
                "{},{},{},{},{},\"{}\"\n",
                event.timestamp.to_rfc3339(),
                event.event_type,
                event.request_id,
                event.pattern_id,
                event.actor,
                details_str.replace('"', "\"\"")
            ));
        }

        Ok(csv)
    }

    /// Export as Markdown.
    fn export_markdown(
        &self,
        events: &[AuditEvent],
        requests: &[ApprovalRequest],
    ) -> Result<String, AphoriaError> {
        let mut md = String::new();

        md.push_str("# Governance Audit Report\n\n");
        md.push_str(&format!("Generated: {}\n\n", Utc::now().format("%Y-%m-%d %H:%M:%S UTC")));

        // Summary
        md.push_str("## Summary\n\n");
        md.push_str(&format!("- Total Events: {}\n", events.len()));
        md.push_str(&format!("- Total Requests: {}\n", requests.len()));

        let approved = requests.iter().filter(|r| r.status.is_approved()).count();
        let rejected = requests.iter().filter(|r| r.status.is_rejected()).count();
        let pending = requests.iter().filter(|r| r.status.is_pending()).count();

        md.push_str(&format!("- Approved: {}\n", approved));
        md.push_str(&format!("- Rejected: {}\n", rejected));
        md.push_str(&format!("- Pending: {}\n\n", pending));

        // Events table
        md.push_str("## Audit Events\n\n");
        md.push_str("| Timestamp | Type | Request | Pattern | Actor |\n");
        md.push_str("|-----------|------|---------|---------|-------|\n");

        for event in events {
            md.push_str(&format!(
                "| {} | {} | {} | {} | {} |\n",
                event.timestamp.format("%Y-%m-%d %H:%M"),
                event.event_type,
                &event.request_id.to_string()[..8],
                &event.pattern_id.to_string()[..8],
                event.actor,
            ));
        }

        md.push('\n');

        // Request details
        md.push_str("## Request Details\n\n");

        for request in requests {
            md.push_str(&format!("### {} ({})\n\n", request.pattern_name, request.id));
            md.push_str(&format!("- **Workflow**: {}\n", request.workflow_name));
            md.push_str(&format!("- **Status**: {}\n", request.status));
            md.push_str(&format!(
                "- **Created**: {} by {}\n",
                request.created_at.format("%Y-%m-%d %H:%M"),
                request.created_by
            ));
            md.push_str(&format!(
                "- **Updated**: {}\n\n",
                request.updated_at.format("%Y-%m-%d %H:%M")
            ));

            if !request.decisions.is_empty() {
                md.push_str("**Decisions:**\n\n");
                for decision in &request.decisions {
                    let comment = decision.comment.as_deref().unwrap_or("-");
                    md.push_str(&format!(
                        "- {} at {}: {} by {} ({})\n",
                        decision.timestamp.format("%Y-%m-%d %H:%M"),
                        decision.stage,
                        decision.decision,
                        decision.approver,
                        comment
                    ));
                }
                md.push('\n');
            }
        }

        Ok(md)
    }

    /// Export events within a date range.
    pub fn export_date_range(
        &self,
        format: ExportFormat,
        output: &Path,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    ) -> Result<(), AphoriaError> {
        let events: Vec<AuditEvent> = self
            .get_all_events()?
            .into_iter()
            .filter(|e| e.timestamp >= start && e.timestamp <= end)
            .collect();

        let store = GovernanceStore::open_default()?;
        let requests: Vec<ApprovalRequest> = store
            .list_all()?
            .into_iter()
            .filter(|r| r.created_at >= start && r.created_at <= end)
            .collect();

        let content = match format {
            ExportFormat::Json => self.export_json(&events, &requests)?,
            ExportFormat::Csv => self.export_csv(&events)?,
            ExportFormat::Markdown => self.export_markdown(&events, &requests)?,
        };

        fs::write(output, content)
            .map_err(|e| AphoriaError::Storage(format!("Failed to write export: {}", e)))?;

        Ok(())
    }
}

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

    #[test]
    fn test_audit_event_creation() {
        let event = AuditEvent::new(
            AuditEventType::StageApproved,
            Uuid::new_v4(),
            Uuid::new_v4(),
            "alice".to_string(),
            serde_json::json!({"stage": "security_review"}),
        );

        assert_eq!(event.event_type, AuditEventType::StageApproved);
        assert_eq!(event.actor, "alice");
    }

    #[test]
    fn test_log_and_retrieve_events() {
        let temp = TempDir::new().expect("temp dir");
        let trail = AuditTrail::new(temp.path()).expect("create trail");

        let event = AuditEvent::new(
            AuditEventType::RequestCreated,
            Uuid::new_v4(),
            Uuid::new_v4(),
            "test_user".to_string(),
            serde_json::json!({}),
        );

        trail.log_event(event.clone()).expect("log");

        let events = trail.get_all_events().expect("get all");
        assert_eq!(events.len(), 1);
        assert_eq!(events[0].id, event.id);
    }

    #[test]
    fn test_pattern_timeline() {
        let temp = TempDir::new().expect("temp dir");
        let trail = AuditTrail::new(temp.path()).expect("create trail");

        let pattern_id = Uuid::new_v4();
        let other_pattern = Uuid::new_v4();

        // Log events for pattern
        trail
            .log_event(AuditEvent::new(
                AuditEventType::RequestCreated,
                Uuid::new_v4(),
                pattern_id,
                "user".to_string(),
                serde_json::json!({}),
            ))
            .expect("log 1");

        trail
            .log_event(AuditEvent::new(
                AuditEventType::StageApproved,
                Uuid::new_v4(),
                pattern_id,
                "user".to_string(),
                serde_json::json!({}),
            ))
            .expect("log 2");

        // Log event for other pattern
        trail
            .log_event(AuditEvent::new(
                AuditEventType::RequestCreated,
                Uuid::new_v4(),
                other_pattern,
                "user".to_string(),
                serde_json::json!({}),
            ))
            .expect("log 3");

        let timeline = trail.get_pattern_timeline(&pattern_id).expect("get timeline");
        assert_eq!(timeline.len(), 2);
    }

    #[test]
    fn test_export_format_parsing() {
        assert_eq!("json".parse::<ExportFormat>().ok(), Some(ExportFormat::Json));
        assert_eq!("CSV".parse::<ExportFormat>().ok(), Some(ExportFormat::Csv));
        assert_eq!("markdown".parse::<ExportFormat>().ok(), Some(ExportFormat::Markdown));
        assert_eq!("md".parse::<ExportFormat>().ok(), Some(ExportFormat::Markdown));
        assert!("unknown".parse::<ExportFormat>().is_err());
    }

    #[test]
    fn test_export_csv() {
        let temp = TempDir::new().expect("temp dir");
        let trail = AuditTrail::new(temp.path()).expect("create trail");

        trail
            .log_event(AuditEvent::new(
                AuditEventType::RequestCreated,
                Uuid::new_v4(),
                Uuid::new_v4(),
                "user".to_string(),
                serde_json::json!({"test": "value"}),
            ))
            .expect("log");

        let events = trail.get_all_events().expect("get");
        let csv = trail.export_csv(&events).expect("export csv");

        assert!(csv.contains("timestamp,event_type,request_id"));
        assert!(csv.contains("Request Created"));
    }
}
