//! Governance storage for approval requests and decisions.
//!
//! Uses JSONL (JSON Lines) format for append-only audit logging
//! of approval requests and decisions.

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

use uuid::Uuid;

use super::types::{ApprovalDecision, ApprovalRequest, ApprovalStatus};
use crate::AphoriaError;

/// Get the default governance store directory.
pub fn governance_store_dir() -> PathBuf {
    if let Some(home) = dirs::home_dir() {
        home.join(".aphoria").join("governance")
    } else {
        PathBuf::from(".aphoria/governance")
    }
}

/// Storage for governance requests and decisions.
pub struct GovernanceStore {
    /// Base directory for governance data.
    base_dir: PathBuf,
}

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

    /// Open the default governance store.
    pub fn open_default() -> Result<Self, AphoriaError> {
        Self::new(governance_store_dir())
    }

    /// Path to the requests JSONL file.
    fn requests_path(&self) -> PathBuf {
        self.base_dir.join("requests.jsonl")
    }

    /// Path to the decisions JSONL file.
    fn decisions_path(&self) -> PathBuf {
        self.base_dir.join("decisions.jsonl")
    }

    /// Save an approval request.
    ///
    /// For new requests, appends to the file. For updates, rewrites the file
    /// with the updated request.
    pub fn save_request(&self, request: &ApprovalRequest) -> Result<(), AphoriaError> {
        // Read all existing requests
        let mut requests = self.list_all()?;

        // Find and update existing, or add new
        let mut found = false;
        for existing in &mut requests {
            if existing.id == request.id {
                *existing = request.clone();
                found = true;
                break;
            }
        }

        if !found {
            requests.push(request.clone());
        }

        // Rewrite the file
        self.write_requests(&requests)
    }

    /// Write all requests to file.
    fn write_requests(&self, requests: &[ApprovalRequest]) -> Result<(), AphoriaError> {
        let path = self.requests_path();
        let mut file = File::create(&path)
            .map_err(|e| AphoriaError::Storage(format!("Failed to create requests file: {}", e)))?;

        for request in requests {
            let json = serde_json::to_string(request).map_err(|e| {
                AphoriaError::Storage(format!("Failed to serialize request: {}", e))
            })?;
            writeln!(file, "{}", json)
                .map_err(|e| AphoriaError::Storage(format!("Failed to write request: {}", e)))?;
        }

        Ok(())
    }

    /// Get a request by ID.
    pub fn get_request(&self, id: &Uuid) -> Result<Option<ApprovalRequest>, AphoriaError> {
        let requests = self.list_all()?;
        Ok(requests.into_iter().find(|r| r.id == *id))
    }

    /// Get a request by pattern ID.
    pub fn get_request_by_pattern(
        &self,
        pattern_id: &Uuid,
    ) -> Result<Option<ApprovalRequest>, AphoriaError> {
        let requests = self.list_all()?;
        // Return the most recent request for this pattern
        Ok(requests.into_iter().filter(|r| r.pattern_id == *pattern_id).next_back())
    }

    /// List all pending requests.
    pub fn list_pending(&self) -> Result<Vec<ApprovalRequest>, AphoriaError> {
        let requests = self.list_all()?;
        Ok(requests.into_iter().filter(|r| r.status.is_pending()).collect())
    }

    /// List all requests.
    pub fn list_all(&self) -> Result<Vec<ApprovalRequest>, AphoriaError> {
        let path = self.requests_path();
        if !path.exists() {
            return Ok(Vec::new());
        }

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

        let mut requests = Vec::new();
        for line in reader.lines() {
            let line = line.map_err(|e| {
                AphoriaError::Storage(format!("Failed to read requests line: {}", e))
            })?;

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

            let request: ApprovalRequest = serde_json::from_str(&line)
                .map_err(|e| AphoriaError::Storage(format!("Failed to parse request: {}", e)))?;
            requests.push(request);
        }

        Ok(requests)
    }

    /// Log a decision (append-only audit log).
    pub fn log_decision(&self, decision: &ApprovalDecision) -> Result<(), AphoriaError> {
        let path = self.decisions_path();
        let mut file =
            OpenOptions::new().create(true).append(true).open(&path).map_err(|e| {
                AphoriaError::Storage(format!("Failed to open decisions file: {}", e))
            })?;

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

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

        Ok(())
    }

    /// Get all decisions for a request.
    pub fn get_decisions(&self, request_id: &Uuid) -> Result<Vec<ApprovalDecision>, AphoriaError> {
        let path = self.decisions_path();
        if !path.exists() {
            return Ok(Vec::new());
        }

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

        let mut decisions = Vec::new();
        for line in reader.lines() {
            let line = line.map_err(|e| {
                AphoriaError::Storage(format!("Failed to read decisions line: {}", e))
            })?;

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

            let decision: ApprovalDecision = serde_json::from_str(&line)
                .map_err(|e| AphoriaError::Storage(format!("Failed to parse decision: {}", e)))?;

            if decision.request_id == *request_id {
                decisions.push(decision);
            }
        }

        Ok(decisions)
    }

    /// Get all decisions (for export).
    pub fn get_all_decisions(&self) -> Result<Vec<ApprovalDecision>, AphoriaError> {
        let path = self.decisions_path();
        if !path.exists() {
            return Ok(Vec::new());
        }

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

        let mut decisions = Vec::new();
        for line in reader.lines() {
            let line = line.map_err(|e| {
                AphoriaError::Storage(format!("Failed to read decisions line: {}", e))
            })?;

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

            let decision: ApprovalDecision = serde_json::from_str(&line)
                .map_err(|e| AphoriaError::Storage(format!("Failed to parse decision: {}", e)))?;
            decisions.push(decision);
        }

        Ok(decisions)
    }

    /// Delete a request (for cleanup).
    pub fn delete_request(&self, id: &Uuid) -> Result<bool, AphoriaError> {
        let mut requests = self.list_all()?;
        let len_before = requests.len();
        requests.retain(|r| r.id != *id);

        if requests.len() < len_before {
            self.write_requests(&requests)?;
            Ok(true)
        } else {
            Ok(false)
        }
    }

    /// Get requests past their stage deadline.
    pub fn get_expired_requests(&self) -> Result<Vec<ApprovalRequest>, AphoriaError> {
        let requests = self.list_pending()?;
        Ok(requests.into_iter().filter(|r| r.is_past_deadline()).collect())
    }

    /// Get count of pending requests by workflow.
    pub fn pending_by_workflow(&self) -> Result<Vec<(String, usize)>, AphoriaError> {
        let pending = self.list_pending()?;
        let mut counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();

        for request in pending {
            *counts.entry(request.workflow_name.clone()).or_insert(0) += 1;
        }

        let mut result: Vec<_> = counts.into_iter().collect();
        result.sort_by(|a, b| b.1.cmp(&a.1));
        Ok(result)
    }

    /// Get requests by status.
    pub fn get_by_status(&self, status_filter: &str) -> Result<Vec<ApprovalRequest>, AphoriaError> {
        let requests = self.list_all()?;
        Ok(requests
            .into_iter()
            .filter(|r| match status_filter.to_lowercase().as_str() {
                "pending" => r.status.is_pending(),
                "approved" => matches!(r.status, ApprovalStatus::Approved),
                "rejected" => matches!(r.status, ApprovalStatus::Rejected { .. }),
                "expired" => matches!(r.status, ApprovalStatus::Expired),
                _ => false,
            })
            .collect())
    }
}

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

    fn create_test_store() -> (GovernanceStore, TempDir) {
        let temp = TempDir::new().expect("temp dir");
        let store = GovernanceStore::new(temp.path()).expect("create store");
        (store, temp)
    }

    #[test]
    fn test_store_creation() {
        let (store, _temp) = create_test_store();
        assert!(store.base_dir.exists());
    }

    #[test]
    fn test_save_and_get_request() {
        let (store, _temp) = create_test_store();

        let request =
            ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

        store.save_request(&request).expect("save");
        let loaded = store.get_request(&request.id).expect("get");

        assert!(loaded.is_some());
        let loaded = loaded.unwrap();
        assert_eq!(loaded.id, request.id);
        assert_eq!(loaded.pattern_name, "test_pattern");
    }

    #[test]
    fn test_list_pending() {
        let (store, _temp) = create_test_store();

        // Create pending request
        let pending =
            ApprovalRequest::new(Uuid::new_v4(), "pending_pattern", "workflow", "stage1", "user");
        store.save_request(&pending).expect("save pending");

        // Create approved request
        let mut approved =
            ApprovalRequest::new(Uuid::new_v4(), "approved_pattern", "workflow", "stage1", "user");
        approved.mark_approved();
        store.save_request(&approved).expect("save approved");

        let pending_list = store.list_pending().expect("list pending");
        assert_eq!(pending_list.len(), 1);
        assert_eq!(pending_list[0].pattern_name, "pending_pattern");
    }

    #[test]
    fn test_log_and_get_decisions() {
        let (store, _temp) = create_test_store();

        let request_id = Uuid::new_v4();
        let decision = ApprovalDecision::new(
            request_id,
            "security_review",
            super::super::types::Decision::Approved,
            "alice",
            Some("LGTM".to_string()),
        );

        store.log_decision(&decision).expect("log");

        let decisions = store.get_decisions(&request_id).expect("get");
        assert_eq!(decisions.len(), 1);
        assert_eq!(decisions[0].approver, "alice");
    }

    #[test]
    fn test_update_request() {
        let (store, _temp) = create_test_store();

        let mut request =
            ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

        store.save_request(&request).expect("save initial");

        // Update the request
        request.advance_to_stage("stage2");
        store.save_request(&request).expect("save updated");

        // Verify update
        let loaded = store.get_request(&request.id).expect("get").unwrap();
        assert_eq!(loaded.current_stage_index, 1);
        assert_eq!(loaded.status.current_stage(), Some("stage2"));

        // Verify only one request exists
        let all = store.list_all().expect("list all");
        assert_eq!(all.len(), 1);
    }

    #[test]
    fn test_get_request_by_pattern() {
        let (store, _temp) = create_test_store();

        let pattern_id = Uuid::new_v4();
        let request =
            ApprovalRequest::new(pattern_id, "test_pattern", "workflow", "stage1", "user");

        store.save_request(&request).expect("save");

        let loaded = store.get_request_by_pattern(&pattern_id).expect("get").unwrap();
        assert_eq!(loaded.pattern_id, pattern_id);
    }

    #[test]
    fn test_delete_request() {
        let (store, _temp) = create_test_store();

        let request =
            ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

        store.save_request(&request).expect("save");
        assert!(store.delete_request(&request.id).expect("delete"));
        assert!(store.get_request(&request.id).expect("get").is_none());
    }
}
