//! Core trait definitions for storage abstraction layer

use std::{collections::HashMap, fmt};

use async_trait::async_trait;
use futures::stream::BoxStream;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::{TaskStatus, WorkflowTask};

/// Custom error types for storage operations
#[derive(Error, Debug)]
pub enum StorageError {
    #[error("Task not found: {id}")]
    TaskNotFound { id: String },

    #[error("Connection failed: {reason}")]
    ConnectionFailed { reason: String },

    #[error("Serialization error: {0}")]
    SerializationError(#[from] serde_json::Error),

    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),

    #[error("Authentication failed: {reason}")]
    AuthenticationFailed { reason: String },

    #[error("Permission denied: {operation}")]
    PermissionDenied { operation: String },

    #[error("Storage backend error: {0}")]
    BackendError(#[from] anyhow::Error),
}

/// Result type for storage operations
pub type StorageResult<T> = Result<T, StorageError>;

/// Time range specification for queries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeRange {
    pub start: chrono::DateTime<chrono::Utc>,
    pub end: chrono::DateTime<chrono::Utc>,
}

impl TimeRange {
    /// Create a time range for the last N hours
    pub fn last_hours(hours: i64) -> Self {
        let end = chrono::Utc::now();
        let start = end - chrono::Duration::hours(hours);
        Self { start, end }
    }

    /// Create a time range for the last N days
    pub fn last_days(days: i64) -> Self {
        let end = chrono::Utc::now();
        let start = end - chrono::Duration::days(days);
        Self { start, end }
    }

    /// Check if a timestamp falls within this range
    pub fn contains(&self, timestamp: chrono::DateTime<chrono::Utc>) -> bool {
        timestamp >= self.start && timestamp <= self.end
    }
}

/// Audit log entry for tracking changes
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
    pub id: String,
    pub task_id: String,
    pub operation: String,
    pub old_status: Option<TaskStatus>,
    pub new_status: Option<TaskStatus>,
    pub user_id: Option<String>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub metadata: HashMap<String, String>,
}

impl AuditEntry {
    /// Create a new audit entry for status change
    pub fn status_change(
        task_id: String,
        old_status: Option<TaskStatus>,
        new_status: TaskStatus,
        user_id: Option<String>,
    ) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            task_id,
            operation: "status_change".to_string(),
            old_status,
            new_status: Some(new_status),
            user_id,
            timestamp: chrono::Utc::now(),
            metadata: HashMap::new(),
        }
    }

    /// Create a new audit entry for task creation
    pub fn task_created(task_id: String, user_id: Option<String>) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            task_id,
            operation: "created".to_string(),
            old_status: None,
            new_status: Some(TaskStatus::Pending),
            user_id,
            timestamp: chrono::Utc::now(),
            metadata: HashMap::new(),
        }
    }
}

/// System metrics for monitoring
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metric {
    pub name: String,
    pub value: f64,
    pub tags: HashMap<String, String>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

impl Metric {
    /// Create a counter metric
    pub fn counter(name: &str, value: f64) -> Self {
        Self {
            name: name.to_string(),
            value,
            tags: HashMap::new(),
            timestamp: chrono::Utc::now(),
        }
    }

    /// Create a gauge metric
    pub fn gauge(name: &str, value: f64) -> Self {
        Self {
            name: name.to_string(),
            value,
            tags: HashMap::new(),
            timestamp: chrono::Utc::now(),
        }
    }

    /// Add a tag to the metric
    pub fn with_tag(mut self, key: &str, value: &str) -> Self {
        self.tags.insert(key.to_string(), value.to_string());
        self
    }
}

/// Authentication token for user sessions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthToken {
    pub token: String,
    pub user_id: String,
    pub expires_at: chrono::DateTime<chrono::Utc>,
    pub permissions: Vec<String>,
}

impl AuthToken {
    /// Check if token is expired
    pub fn is_expired(&self) -> bool {
        chrono::Utc::now() > self.expires_at
    }

    /// Check if token has specific permission
    pub fn has_permission(&self, permission: &str) -> bool {
        self.permissions.contains(&permission.to_string())
    }
}

/// Real-time task events for subscriptions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskEvent {
    pub event_type: TaskEventType,
    pub task_id: String,
    pub task: Option<WorkflowTask>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub metadata: HashMap<String, String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TaskEventType {
    Created,
    StatusChanged,
    Updated,
    Deleted,
    Enqueued,
    Dequeued,
    Completed,
    Failed,
}

impl fmt::Display for TaskEventType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TaskEventType::Created => write!(f, "created"),
            TaskEventType::StatusChanged => write!(f, "status_changed"),
            TaskEventType::Updated => write!(f, "updated"),
            TaskEventType::Deleted => write!(f, "deleted"),
            TaskEventType::Enqueued => write!(f, "enqueued"),
            TaskEventType::Dequeued => write!(f, "dequeued"),
            TaskEventType::Completed => write!(f, "completed"),
            TaskEventType::Failed => write!(f, "failed"),
        }
    }
}

/// Core storage trait for task management
///
/// This trait provides a unified interface for different storage backends
/// while maintaining compatibility with the existing TaskQueue interface.
#[async_trait]
pub trait TaskStorage: Send + Sync {
    /// Initialize the storage backend
    ///
    /// This method should set up necessary connections, create
    /// tables/collections, and perform any required initialization.
    async fn init(&self) -> StorageResult<()>;

    /// Health check for the storage backend
    async fn health_check(&self) -> StorageResult<bool>;

    // Core Task Operations (Backwards Compatible with TaskQueue)

    /// Add a task to the queue with priority
    async fn enqueue(&self, task: WorkflowTask, priority: u8) -> StorageResult<()>;

    /// Remove and return the highest priority task from the queue
    async fn dequeue(&self) -> StorageResult<Option<WorkflowTask>>;

    /// Peek at the next task without removing it
    async fn peek(&self) -> StorageResult<Option<WorkflowTask>>;

    /// Get a specific task by ID
    async fn get_task(&self, id: &str) -> StorageResult<Option<WorkflowTask>>;

    /// Update an existing task
    async fn update_task(&self, task: WorkflowTask) -> StorageResult<()>;

    /// Update only the status of a task
    async fn update_task_status(&self, id: &str, status: TaskStatus) -> StorageResult<()>;

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

    /// Requeue a failed task for retry
    async fn requeue_failed_task(&self, id: &str) -> StorageResult<bool>;

    /// Get current queue size
    async fn queue_size(&self) -> StorageResult<usize>;

    /// Get total number of tasks
    async fn total_tasks(&self) -> StorageResult<usize>;

    /// Clean up completed tasks older than specified duration
    async fn cleanup_completed_tasks(&self, older_than: chrono::Duration) -> StorageResult<usize>;

    // Advanced Features for Future Expansion

    /// Get audit log entries for a time range
    async fn get_audit_log(&self, time_range: TimeRange) -> StorageResult<Vec<AuditEntry>>;

    /// Record an audit entry
    async fn record_audit_entry(&self, entry: AuditEntry) -> StorageResult<()>;

    /// Get system metrics for monitoring
    async fn get_metrics(&self, time_range: TimeRange) -> StorageResult<Vec<Metric>>;

    /// Record a system metric
    async fn record_metric(&self, metric: Metric) -> StorageResult<()>;

    /// Authenticate a user and return a token
    async fn authenticate(&self, username: &str, password: &str) -> StorageResult<AuthToken>;

    /// Validate an authentication token
    async fn validate_token(&self, token: &str) -> StorageResult<Option<AuthToken>>;

    /// Subscribe to real-time task events
    async fn subscribe_to_events(
        &self,
        filters: Option<Vec<TaskEventType>>,
    ) -> StorageResult<BoxStream<'_, TaskEvent>>;

    /// Publish a task event to subscribers
    async fn publish_event(&self, event: TaskEvent) -> StorageResult<()>;

    // Bulk Operations for Efficiency

    /// Enqueue multiple tasks in a single operation
    async fn enqueue_batch(&self, tasks: Vec<(WorkflowTask, u8)>) -> StorageResult<()> {
        // Default implementation using individual enqueue calls
        // Implementations can override for better performance
        for (task, priority) in tasks {
            self.enqueue(task, priority).await?;
        }
        Ok(())
    }

    /// Get multiple tasks by their IDs
    async fn get_tasks_batch(
        &self,
        ids: Vec<String>,
    ) -> StorageResult<HashMap<String, WorkflowTask>> {
        // Default implementation using individual get calls
        // Implementations can override for better performance
        let mut result = HashMap::new();
        for id in ids {
            if let Some(task) = self.get_task(&id).await? {
                result.insert(id, task);
            }
        }
        Ok(result)
    }

    /// Update multiple tasks in a single operation
    async fn update_tasks_batch(&self, tasks: Vec<WorkflowTask>) -> StorageResult<()> {
        // Default implementation using individual update calls
        // Implementations can override for better performance
        for task in tasks {
            self.update_task(task).await?;
        }
        Ok(())
    }

    // Advanced Query Operations

    /// 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>>;

    /// Get task dependency graph
    async fn get_task_dependencies(&self, task_id: &str) -> StorageResult<Vec<String>>;

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

    // Backup and Recovery

    /// Create a backup of the storage
    async fn create_backup(&self, backup_path: &str) -> StorageResult<()>;

    /// Restore from a backup
    async fn restore_from_backup(&self, backup_path: &str) -> StorageResult<()>;

    /// Get storage statistics
    async fn get_statistics(&self) -> StorageResult<StorageStatistics>;
}

/// Storage backend statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageStatistics {
    pub total_tasks: usize,
    pub pending_tasks: usize,
    pub running_tasks: usize,
    pub completed_tasks: usize,
    pub failed_tasks: usize,
    pub total_storage_size: u64,
    pub last_cleanup: Option<chrono::DateTime<chrono::Utc>>,
    pub uptime: chrono::Duration,
}

impl StorageStatistics {
    pub fn new() -> Self {
        Self {
            total_tasks: 0,
            pending_tasks: 0,
            running_tasks: 0,
            completed_tasks: 0,
            failed_tasks: 0,
            total_storage_size: 0,
            last_cleanup: None,
            uptime: chrono::Duration::seconds(0),
        }
    }
}

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