//! Filesystem-based storage implementation for TaskStorage trait
//!
//! This module provides a filesystem-backed implementation of the TaskStorage
//! trait, maintaining full backwards compatibility with the original TaskQueue
//! implementation.

use std::{
    collections::{HashMap, VecDeque},
    path::{Path, PathBuf},
    sync::Arc,
};

use anyhow::{Context, Result};
use async_trait::async_trait;
use futures::stream::{self, BoxStream};
use serde::{Deserialize, Serialize};
use tokio::{fs, sync::RwLock};
use tracing::{debug, info, warn};

use super::traits::{
    AuditEntry, AuthToken, Metric, StorageError, StorageResult, StorageStatistics, TaskEvent,
    TaskEventType, TaskStorage, TimeRange,
};
use crate::{TaskStatus, WorkflowTask};

/// Directory names for filesystem storage
const TASKS_DIR: &str = "tasks";
const QUEUE_DIR: &str = "queue";

/// Task queue entry with priority and retry logic
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueuedTask {
    pub id: String,
    pub priority: u8,
    pub enqueued_at: chrono::DateTime<chrono::Utc>,
    pub retry_count: u8,
    pub max_retries: u8,
}

/// Filesystem-based storage implementation
///
/// This implementation stores tasks as JSON files in a directory structure:
/// - `data_dir/tasks/` - Individual task JSON files
/// - `data_dir/queue/` - Queue entry JSON files for priority ordering
pub struct FilesystemStorage {
    data_dir: PathBuf,
    in_memory_queue: Arc<RwLock<VecDeque<QueuedTask>>>,
    tasks: Arc<RwLock<HashMap<String, WorkflowTask>>>,
    start_time: chrono::DateTime<chrono::Utc>,
}

impl FilesystemStorage {
    /// Create a new filesystem storage instance
    ///
    /// # Arguments
    /// * `data_dir` - The root directory for storing task data
    ///
    /// # Returns
    /// A new FilesystemStorage instance ready for use
    pub async fn new<P: AsRef<Path>>(data_dir: P) -> Result<Self> {
        let data_dir = data_dir.as_ref().to_path_buf();

        // Create required directories
        fs::create_dir_all(&data_dir)
            .await
            .context("Failed to create data directory")?;
        fs::create_dir_all(data_dir.join(TASKS_DIR))
            .await
            .context("Failed to create tasks directory")?;
        fs::create_dir_all(data_dir.join(QUEUE_DIR))
            .await
            .context("Failed to create queue directory")?;

        let storage = Self {
            data_dir,
            in_memory_queue: Arc::new(RwLock::new(VecDeque::new())),
            tasks: Arc::new(RwLock::new(HashMap::new())),
            start_time: chrono::Utc::now(),
        };

        // Load existing data from persistent storage
        storage.load_from_storage().await?;

        Ok(storage)
    }

    /// Load existing tasks and queue entries from filesystem
    async fn load_from_storage(&self) -> Result<()> {
        let mut tasks = self.tasks.write().await;
        let mut queue = self.in_memory_queue.write().await;

        // Load all tasks
        let tasks_dir = self.data_dir.join(TASKS_DIR);
        if tasks_dir.exists() {
            let mut entries = fs::read_dir(&tasks_dir).await?;
            while let Some(entry) = entries.next_entry().await? {
                if !entry.file_type().await?.is_file() {
                    continue;
                }
                let file_name = entry.file_name();
                let Some(task_id) = file_name.to_str() else {
                    continue;
                };
                if !task_id.ends_with(".json") {
                    continue;
                }
                let task_id = task_id
                    .strip_suffix(".json")
                    .context("Failed to strip .json suffix")?;
                let content = fs::read_to_string(entry.path()).await?;
                let task: WorkflowTask =
                    serde_json::from_str(&content).context("Failed to deserialize task")?;
                tasks.insert(task_id.to_string(), task);
            }
        }

        // Load queue entries
        let queue_dir = self.data_dir.join(QUEUE_DIR);
        if queue_dir.exists() {
            let mut entries = fs::read_dir(&queue_dir).await?;
            while let Some(entry) = entries.next_entry().await? {
                if entry.file_type().await?.is_file() {
                    let content = fs::read_to_string(entry.path()).await?;
                    let queued_task: QueuedTask = serde_json::from_str(&content)
                        .context("Failed to deserialize queued task")?;
                    queue.push_back(queued_task);
                }
            }
        }

        // Sort queue by priority (higher priority first) and enqueue time
        let mut sorted_queue: Vec<_> = queue.drain(..).collect();
        sorted_queue.sort_by(|a, b| {
            b.priority
                .cmp(&a.priority)
                .then_with(|| a.enqueued_at.cmp(&b.enqueued_at))
        });

        queue.extend(sorted_queue);

        info!(
            "Loaded {} tasks and {} queue entries from storage",
            tasks.len(),
            queue.len()
        );

        Ok(())
    }
}

#[async_trait]
impl TaskStorage for FilesystemStorage {
    /// Initialize the storage backend
    async fn init(&self) -> StorageResult<()> {
        // Storage is already initialized in new()
        // This method is mainly for database connections, schema creation, etc.
        Ok(())
    }

    /// Health check for filesystem storage
    async fn health_check(&self) -> StorageResult<bool> {
        // Check if we can write to the data directory
        let test_file = self.data_dir.join(".health_check");

        // Write test file
        if let Err(e) = fs::write(&test_file, "test").await {
            return Err(StorageError::IoError(e));
        }

        // Remove test file
        if let Err(e) = fs::remove_file(&test_file).await {
            return Err(StorageError::IoError(e));
        }

        Ok(true)
    }

    /// Add a task to the queue with priority
    async fn enqueue(&self, task: WorkflowTask, priority: u8) -> StorageResult<()> {
        let task_id = task.id.clone();

        // Store task to file
        let task_path = self
            .data_dir
            .join(TASKS_DIR)
            .join(format!("{}.json", task_id));
        let task_data =
            serde_json::to_string_pretty(&task).map_err(StorageError::SerializationError)?;
        fs::write(&task_path, task_data)
            .await
            .context("Failed to write task to storage")
            .map_err(StorageError::BackendError)?;

        // Add to in-memory tasks
        {
            let mut tasks = self.tasks.write().await;
            tasks.insert(task_id.clone(), task);
        }

        // Create queue entry
        let queued_task = QueuedTask {
            id: task_id.clone(),
            priority,
            enqueued_at: chrono::Utc::now(),
            retry_count: 0,
            max_retries: 3,
        };

        // Store queue entry to file
        let queue_path = self
            .data_dir
            .join(QUEUE_DIR)
            .join(format!("{}.json", task_id));
        let queue_data =
            serde_json::to_string_pretty(&queued_task).map_err(StorageError::SerializationError)?;
        fs::write(&queue_path, queue_data)
            .await
            .context("Failed to write queue entry to storage")
            .map_err(StorageError::BackendError)?;

        // Add to in-memory queue with priority ordering
        {
            let mut queue = self.in_memory_queue.write().await;

            // Find the right position to insert based on priority
            let insert_pos = queue
                .iter()
                .position(|item| item.priority < priority)
                .unwrap_or(queue.len());

            queue.insert(insert_pos, queued_task);
        }

        debug!("Enqueued task {} with priority {}", task_id, priority);
        Ok(())
    }

    /// Remove and return the highest priority task from the queue
    async fn dequeue(&self) -> StorageResult<Option<WorkflowTask>> {
        let queued_task = {
            let mut queue = self.in_memory_queue.write().await;
            queue.pop_front()
        };

        match queued_task {
            Some(queued) => {
                // Remove from persistent queue
                let queue_path = self
                    .data_dir
                    .join(QUEUE_DIR)
                    .join(format!("{}.json", queued.id));
                if queue_path.exists() {
                    fs::remove_file(&queue_path)
                        .await
                        .context("Failed to remove queue entry from storage")
                        .map_err(StorageError::BackendError)?;
                }

                // Get the actual task
                let tasks = self.tasks.read().await;
                if let Some(task) = tasks.get(&queued.id) {
                    debug!("Dequeued task {}", queued.id);
                    Ok(Some(task.clone()))
                } else {
                    warn!("Queued task {} not found in task storage", queued.id);
                    Ok(None)
                }
            }
            None => Ok(None),
        }
    }

    /// Peek at the next task without removing it
    async fn peek(&self) -> StorageResult<Option<WorkflowTask>> {
        let queue = self.in_memory_queue.read().await;

        if let Some(queued) = queue.front() {
            let tasks = self.tasks.read().await;
            if let Some(task) = tasks.get(&queued.id) {
                Ok(Some(task.clone()))
            } else {
                warn!("Queued task {} not found in task storage", queued.id);
                Ok(None)
            }
        } else {
            Ok(None)
        }
    }

    /// Get a specific task by ID
    async fn get_task(&self, id: &str) -> StorageResult<Option<WorkflowTask>> {
        let tasks = self.tasks.read().await;
        Ok(tasks.get(id).cloned())
    }

    /// Update an existing task
    async fn update_task(&self, task: WorkflowTask) -> StorageResult<()> {
        let task_id = task.id.clone();

        // Update persistent storage
        let task_path = self
            .data_dir
            .join(TASKS_DIR)
            .join(format!("{}.json", task_id));
        let task_data =
            serde_json::to_string_pretty(&task).map_err(StorageError::SerializationError)?;
        fs::write(&task_path, task_data)
            .await
            .context("Failed to update task in storage")
            .map_err(StorageError::BackendError)?;

        // Update in-memory storage
        {
            let mut tasks = self.tasks.write().await;
            tasks.insert(task_id.clone(), task);
        }

        debug!("Updated task {}", task_id);
        Ok(())
    }

    /// Update only the status of a task
    async fn update_task_status(&self, id: &str, status: TaskStatus) -> StorageResult<()> {
        let mut task = {
            let tasks = self.tasks.read().await;
            tasks.get(id).cloned()
        };

        if let Some(ref mut task) = task {
            task.status = status.clone();

            match status {
                TaskStatus::Running => task.started_at = Some(chrono::Utc::now()),
                TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled => {
                    task.completed_at = Some(chrono::Utc::now());
                }
                _ => {}
            }

            self.update_task(task.clone()).await?;
            debug!("Updated task {} status to {:?}", id, status);
        }

        Ok(())
    }

    /// List tasks with optional status filter
    async fn list_tasks(
        &self,
        status_filter: Option<TaskStatus>,
    ) -> StorageResult<Vec<WorkflowTask>> {
        let tasks = self.tasks.read().await;

        let result = tasks
            .values()
            .filter(|task| {
                status_filter
                    .as_ref()
                    .map(|filter| &task.status == filter)
                    .unwrap_or(true)
            })
            .cloned()
            .collect();

        Ok(result)
    }

    /// Requeue a failed task for retry
    async fn requeue_failed_task(&self, id: &str) -> StorageResult<bool> {
        let queue_path = self.data_dir.join(QUEUE_DIR).join(format!("{}.json", id));

        let queued_task = if queue_path.exists() {
            let content = fs::read_to_string(&queue_path)
                .await
                .context("Failed to read queue entry")
                .map_err(StorageError::BackendError)?;
            let mut queued: QueuedTask =
                serde_json::from_str(&content).map_err(StorageError::SerializationError)?;

            if queued.retry_count < queued.max_retries {
                queued.retry_count += 1;
                queued.enqueued_at = chrono::Utc::now();

                // Update in storage
                let queue_data = serde_json::to_string_pretty(&queued)
                    .map_err(StorageError::SerializationError)?;
                fs::write(&queue_path, queue_data)
                    .await
                    .context("Failed to update queue entry")
                    .map_err(StorageError::BackendError)?;

                Some(queued)
            } else {
                warn!("Task {} exceeded max retries ({})", id, queued.max_retries);
                None
            }
        } else {
            None
        };

        if let Some(queued) = queued_task {
            // Reset task status to pending
            self.update_task_status(id, TaskStatus::Pending).await?;

            // Add back to queue
            {
                let mut queue = self.in_memory_queue.write().await;
                let insert_pos = queue
                    .iter()
                    .position(|item| item.priority < queued.priority)
                    .unwrap_or(queue.len());
                queue.insert(insert_pos, queued);
            }

            info!("Requeued failed task {} for retry", id);
            Ok(true)
        } else {
            Ok(false)
        }
    }

    /// Get current queue size
    async fn queue_size(&self) -> StorageResult<usize> {
        let queue = self.in_memory_queue.read().await;
        Ok(queue.len())
    }

    /// Get total number of tasks
    async fn total_tasks(&self) -> StorageResult<usize> {
        let tasks = self.tasks.read().await;
        Ok(tasks.len())
    }

    /// Clean up completed tasks older than specified duration
    async fn cleanup_completed_tasks(&self, older_than: chrono::Duration) -> StorageResult<usize> {
        let cutoff = chrono::Utc::now() - older_than;
        let mut removed_count = 0;

        let tasks_to_remove: Vec<String> = {
            let tasks = self.tasks.read().await;
            tasks
                .values()
                .filter(|task| {
                    matches!(
                        task.status,
                        TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
                    ) && task.completed_at.map(|t| t < cutoff).unwrap_or(false)
                })
                .map(|task| task.id.clone())
                .collect()
        };

        for task_id in tasks_to_remove {
            // Remove from persistent storage
            let task_path = self
                .data_dir
                .join(TASKS_DIR)
                .join(format!("{}.json", task_id));
            let queue_path = self
                .data_dir
                .join(QUEUE_DIR)
                .join(format!("{}.json", task_id));

            if task_path.exists() {
                fs::remove_file(&task_path)
                    .await
                    .map_err(StorageError::IoError)?;
            }
            if queue_path.exists() {
                fs::remove_file(&queue_path)
                    .await
                    .map_err(StorageError::IoError)?;
            }

            // Remove from in-memory storage
            {
                let mut tasks = self.tasks.write().await;
                tasks.remove(&task_id);
            }

            removed_count += 1;
        }

        if removed_count > 0 {
            info!(
                "Cleaned up {} completed tasks older than {:?}",
                removed_count, older_than
            );
        }

        Ok(removed_count)
    }

    // Advanced features - stub implementations for future expansion

    /// Get audit log entries for a time range
    async fn get_audit_log(&self, _time_range: TimeRange) -> StorageResult<Vec<AuditEntry>> {
        // Stub implementation - could write to filesystem if desired
        Ok(vec![])
    }

    /// Record an audit entry
    async fn record_audit_entry(&self, _entry: AuditEntry) -> StorageResult<()> {
        // Stub implementation - could write to filesystem if desired
        Ok(())
    }

    /// Get system metrics for monitoring
    async fn get_metrics(&self, _time_range: TimeRange) -> StorageResult<Vec<Metric>> {
        // Basic metrics from current data
        let queue_size = self.queue_size().await?;
        let total_tasks = self.total_tasks().await?;

        let metrics = vec![
            Metric::gauge("queue_size", queue_size as f64),
            Metric::gauge("total_tasks", total_tasks as f64),
        ];

        Ok(metrics)
    }

    /// Record a system metric
    async fn record_metric(&self, _metric: Metric) -> StorageResult<()> {
        // Stub implementation
        Ok(())
    }

    /// Authenticate a user and return a token
    async fn authenticate(&self, _username: &str, _password: &str) -> StorageResult<AuthToken> {
        // Return default admin token for compatibility
        Ok(AuthToken {
            token: uuid::Uuid::new_v4().to_string(),
            user_id: "admin".to_string(),
            expires_at: chrono::Utc::now() + chrono::Duration::hours(24),
            permissions: vec!["admin".to_string()],
        })
    }

    /// Validate an authentication token
    async fn validate_token(&self, token: &str) -> StorageResult<Option<AuthToken>> {
        // Stub implementation - always validates for now
        if !token.is_empty() {
            Ok(Some(AuthToken {
                token: token.to_string(),
                user_id: "admin".to_string(),
                expires_at: chrono::Utc::now() + chrono::Duration::hours(24),
                permissions: vec!["admin".to_string()],
            }))
        } else {
            Ok(None)
        }
    }

    /// Subscribe to real-time task events
    async fn subscribe_to_events(
        &self,
        _filters: Option<Vec<TaskEventType>>,
    ) -> StorageResult<BoxStream<'_, TaskEvent>> {
        // Return empty stream for now - could implement with file watching
        let stream = stream::empty();
        Ok(Box::pin(stream))
    }

    /// Publish a task event to subscribers
    async fn publish_event(&self, _event: TaskEvent) -> StorageResult<()> {
        // Stub implementation
        Ok(())
    }

    // Advanced Query Operations - providing basic implementations

    /// Search tasks by various criteria
    async fn search_tasks(
        &self,
        name_pattern: Option<String>,
        status_filter: Option<Vec<TaskStatus>>,
        created_after: Option<chrono::DateTime<chrono::Utc>>,
        created_before: Option<chrono::DateTime<chrono::Utc>>,
        limit: Option<usize>,
        _offset: Option<usize>,
    ) -> StorageResult<Vec<WorkflowTask>> {
        let tasks = self.tasks.read().await;

        let mut result: Vec<WorkflowTask> = tasks
            .values()
            .filter(|task| {
                // Name pattern filter
                if let Some(ref pattern) = name_pattern {
                    if !task.name.contains(pattern) {
                        return false;
                    }
                }

                // Status filter
                if let Some(ref statuses) = status_filter {
                    if !statuses.contains(&task.status) {
                        return false;
                    }
                }

                // Created after filter
                if let Some(after) = created_after {
                    if task.created_at < after {
                        return false;
                    }
                }

                // Created before filter
                if let Some(before) = created_before {
                    if task.created_at > before {
                        return false;
                    }
                }

                true
            })
            .cloned()
            .collect();

        // Apply limit
        if let Some(limit_val) = limit {
            result.truncate(limit_val);
        }

        Ok(result)
    }

    /// Get task dependency graph
    async fn get_task_dependencies(&self, task_id: &str) -> StorageResult<Vec<String>> {
        let tasks = self.tasks.read().await;
        if let Some(task) = tasks.get(task_id) {
            Ok(task.dependencies.clone())
        } else {
            Err(StorageError::TaskNotFound {
                id: task_id.to_string(),
            })
        }
    }

    /// Get tasks that depend on the given task
    async fn get_dependent_tasks(&self, task_id: &str) -> StorageResult<Vec<String>> {
        let tasks = self.tasks.read().await;

        let dependents: Vec<String> = tasks
            .values()
            .filter(|task| task.dependencies.contains(&task_id.to_string()))
            .map(|task| task.id.clone())
            .collect();

        Ok(dependents)
    }

    // Backup and Recovery - stub implementations

    /// Create a backup of the storage
    async fn create_backup(&self, backup_path: &str) -> StorageResult<()> {
        // Could implement by copying the entire data_dir
        let _backup_path = PathBuf::from(backup_path);
        // Stub for now
        Ok(())
    }

    /// Restore from a backup
    async fn restore_from_backup(&self, backup_path: &str) -> StorageResult<()> {
        // Could implement by replacing data_dir contents and reloading
        let _backup_path = PathBuf::from(backup_path);
        // Stub for now
        Ok(())
    }

    /// Get storage statistics
    async fn get_statistics(&self) -> StorageResult<StorageStatistics> {
        let tasks = self.tasks.read().await;

        let mut stats = StorageStatistics::new();
        stats.total_tasks = tasks.len();
        stats.uptime = chrono::Utc::now() - self.start_time;

        // Count tasks by status
        for task in tasks.values() {
            match task.status {
                TaskStatus::Pending => stats.pending_tasks += 1,
                TaskStatus::Running => stats.running_tasks += 1,
                TaskStatus::Completed => stats.completed_tasks += 1,
                TaskStatus::Failed => stats.failed_tasks += 1,
                TaskStatus::Cancelled => {} // Not tracked in base stats
            }
        }

        // Calculate approximate storage size
        if let Ok(metadata) = fs::metadata(&self.data_dir).await {
            stats.total_storage_size = metadata.len();
        }

        Ok(stats)
    }
}
