//! Monitor subcommand - real-time monitoring of contracts.

use clap::Args;
use uuid::Uuid;

/// Arguments for monitor command.
#[derive(Args, Debug)]
pub struct MonitorArgs {
    /// API URL
    #[arg(long, env = "MAKIMA_API_URL", default_value = "https://api.makima.jp")]
    pub api_url: String,

    /// API key for authentication
    #[arg(long, env = "MAKIMA_API_KEY")]
    pub api_key: String,

    /// Contract IDs to monitor (if empty, monitors all active contracts)
    #[arg(index = 1)]
    pub contract_ids: Vec<Uuid>,

    /// Filter by status (active, completed, pending)
    #[arg(long)]
    pub status: Option<String>,

    /// Only show stale contracts (no recent activity)
    #[arg(long)]
    pub stale: bool,

    /// Only show important events (suppress routine updates)
    #[arg(long, short = 'q')]
    pub quiet: bool,

    /// Output format: tui, text, json
    #[arg(long, default_value = "text")]
    pub format: OutputFormat,

    /// Poll interval in seconds
    #[arg(long, default_value = "5")]
    pub poll_interval: u64,

    /// Stale threshold in seconds (contracts with no activity in this time are considered stale)
    #[arg(long, default_value = "300")]
    pub stale_threshold: u64,
}

/// Output format for the monitor command.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OutputFormat {
    /// Full terminal UI dashboard
    Tui,
    /// Plain text event stream
    #[default]
    Text,
    /// JSON event stream (for scripting)
    Json,
}

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

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "tui" => Ok(Self::Tui),
            "text" => Ok(Self::Text),
            "json" => Ok(Self::Json),
            _ => Err(format!(
                "Invalid format '{}'. Expected: tui, text, json",
                s
            )),
        }
    }
}

/// Contract monitoring state for display.
#[derive(Debug, Clone)]
pub struct ContractState {
    pub id: Uuid,
    pub name: String,
    pub status: String,
    pub phase: String,
    pub supervisor_state: Option<String>,
    pub task_count: i32,
    pub running_tasks: i32,
    pub pending_tasks: i32,
    pub completed_tasks: i32,
    pub last_activity: Option<String>,
    pub current_activity: Option<String>,
    pub has_pending_question: bool,
    pub pending_question: Option<String>,
}

impl ContractState {
    /// Parse contract state from API response.
    pub fn from_json(value: &serde_json::Value) -> Option<Self> {
        let id = value
            .get("id")
            .and_then(|v| v.as_str())
            .and_then(|s| Uuid::parse_str(s).ok())?;

        let name = value
            .get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("Unnamed")
            .to_string();

        let status = value
            .get("status")
            .and_then(|v| v.as_str())
            .unwrap_or("unknown")
            .to_string();

        let phase = value
            .get("phase")
            .and_then(|v| v.as_str())
            .unwrap_or("unknown")
            .to_string();

        // Extract supervisor state from tasks
        let tasks = value.get("tasks").and_then(|v| v.as_array());
        let (supervisor_state, running_tasks, pending_tasks, completed_tasks, task_count) =
            if let Some(tasks) = tasks {
                let mut sv_state = None;
                let mut running = 0;
                let mut pending = 0;
                let mut completed = 0;

                for task in tasks {
                    let task_status = task.get("status").and_then(|v| v.as_str());
                    let role = task.get("role").and_then(|v| v.as_str());

                    if role == Some("supervisor") {
                        sv_state = task_status.map(|s| s.to_string());
                    }

                    match task_status {
                        Some("running") | Some("working") => running += 1,
                        Some("pending") | Some("queued") => pending += 1,
                        Some("done") | Some("completed") | Some("merged") => completed += 1,
                        _ => {}
                    }
                }

                (sv_state, running, pending, completed, tasks.len() as i32)
            } else {
                (None, 0, 0, 0, 0)
            };

        // Extract last activity time
        let last_activity = value
            .get("updatedAt")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        // Check for pending questions
        let pending_question_value = value.get("pendingQuestion");
        let has_pending_question = pending_question_value
            .map(|v| !v.is_null())
            .unwrap_or(false);
        let pending_question = pending_question_value
            .and_then(|v| v.get("question"))
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        // Get current activity from supervisor or most recent task
        let current_activity = tasks.and_then(|tasks| {
            // Find the most recently updated running task
            tasks
                .iter()
                .filter(|t| {
                    let status = t.get("status").and_then(|v| v.as_str());
                    status == Some("running") || status == Some("working")
                })
                .max_by_key(|t| t.get("updatedAt").and_then(|v| v.as_str()))
                .and_then(|t| {
                    let name = t.get("name").and_then(|v| v.as_str())?;
                    let progress = t.get("progressSummary").and_then(|v| v.as_str());
                    Some(match progress {
                        Some(p) if !p.is_empty() => format!("{}: {}", name, p),
                        _ => name.to_string(),
                    })
                })
        });

        Some(Self {
            id,
            name,
            status,
            phase,
            supervisor_state,
            task_count,
            running_tasks,
            pending_tasks,
            completed_tasks,
            last_activity,
            current_activity,
            has_pending_question,
            pending_question,
        })
    }

    /// Check if this contract is stale (no recent activity).
    pub fn is_stale(&self, threshold_secs: u64) -> bool {
        if let Some(ref last) = self.last_activity {
            if let Ok(last_time) = chrono::DateTime::parse_from_rfc3339(last) {
                let now = chrono::Utc::now();
                let duration = now.signed_duration_since(last_time);
                return duration.num_seconds() > threshold_secs as i64;
            }
        }
        // If we can't parse the time, assume it's stale
        true
    }

    /// Get a status symbol for display.
    pub fn status_symbol(&self) -> &'static str {
        if self.has_pending_question {
            "?"
        } else if self.running_tasks > 0 {
            "*"
        } else if self.status == "completed" {
            "+"
        } else if self.status == "active" {
            "-"
        } else {
            " "
        }
    }

    /// Get a colored status for terminal output.
    pub fn status_color(&self) -> &'static str {
        if self.has_pending_question {
            "\x1b[33m" // Yellow
        } else if self.running_tasks > 0 {
            "\x1b[32m" // Green
        } else if self.status == "completed" {
            "\x1b[34m" // Blue
        } else if self.status == "active" {
            "\x1b[0m" // Default
        } else {
            "\x1b[90m" // Gray
        }
    }
}

/// Event emitted during monitoring.
#[derive(Debug, Clone, serde::Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MonitorEvent {
    /// Initial snapshot of contract state
    Snapshot {
        contracts: Vec<ContractSnapshot>,
        timestamp: String,
    },
    /// Contract status changed
    StatusChange {
        contract_id: Uuid,
        contract_name: String,
        old_status: String,
        new_status: String,
        timestamp: String,
    },
    /// Task started
    TaskStarted {
        contract_id: Uuid,
        contract_name: String,
        task_id: Uuid,
        task_name: String,
        timestamp: String,
    },
    /// Task completed
    TaskCompleted {
        contract_id: Uuid,
        contract_name: String,
        task_id: Uuid,
        task_name: String,
        result: String,
        timestamp: String,
    },
    /// Question pending (needs user input)
    QuestionPending {
        contract_id: Uuid,
        contract_name: String,
        question: String,
        timestamp: String,
    },
    /// Phase changed
    PhaseChange {
        contract_id: Uuid,
        contract_name: String,
        old_phase: String,
        new_phase: String,
        timestamp: String,
    },
    /// Contract became stale
    ContractStale {
        contract_id: Uuid,
        contract_name: String,
        last_activity: String,
        timestamp: String,
    },
    /// Error during monitoring
    Error {
        message: String,
        timestamp: String,
    },
}

/// Snapshot of contract state for events.
#[derive(Debug, Clone, serde::Serialize)]
pub struct ContractSnapshot {
    pub id: Uuid,
    pub name: String,
    pub status: String,
    pub phase: String,
    pub running_tasks: i32,
    pub pending_tasks: i32,
    pub completed_tasks: i32,
    pub has_pending_question: bool,
}

impl From<&ContractState> for ContractSnapshot {
    fn from(state: &ContractState) -> Self {
        Self {
            id: state.id,
            name: state.name.clone(),
            status: state.status.clone(),
            phase: state.phase.clone(),
            running_tasks: state.running_tasks,
            pending_tasks: state.pending_tasks,
            completed_tasks: state.completed_tasks,
            has_pending_question: state.has_pending_question,
        }
    }
}

impl MonitorEvent {
    /// Format event for text output.
    pub fn to_text(&self) -> String {
        match self {
            Self::Snapshot { contracts, timestamp } => {
                let mut lines = vec![format!("[{}] Monitoring {} contracts:", timestamp, contracts.len())];
                for c in contracts {
                    let status_indicator = if c.has_pending_question {
                        "?"
                    } else if c.running_tasks > 0 {
                        "*"
                    } else {
                        "-"
                    };
                    lines.push(format!(
                        "  {} {} ({}) - {} | tasks: {} running, {} pending, {} done",
                        status_indicator,
                        c.name,
                        c.phase,
                        c.status,
                        c.running_tasks,
                        c.pending_tasks,
                        c.completed_tasks
                    ));
                }
                lines.join("\n")
            }
            Self::StatusChange {
                contract_name,
                old_status,
                new_status,
                timestamp,
                ..
            } => {
                format!(
                    "[{}] {} status: {} -> {}",
                    timestamp, contract_name, old_status, new_status
                )
            }
            Self::TaskStarted {
                contract_name,
                task_name,
                timestamp,
                ..
            } => {
                format!("[{}] {} task started: {}", timestamp, contract_name, task_name)
            }
            Self::TaskCompleted {
                contract_name,
                task_name,
                result,
                timestamp,
                ..
            } => {
                format!(
                    "[{}] {} task completed: {} ({})",
                    timestamp, contract_name, task_name, result
                )
            }
            Self::QuestionPending {
                contract_name,
                question,
                timestamp,
                ..
            } => {
                format!(
                    "[{}] {} NEEDS INPUT: {}",
                    timestamp, contract_name, question
                )
            }
            Self::PhaseChange {
                contract_name,
                old_phase,
                new_phase,
                timestamp,
                ..
            } => {
                format!(
                    "[{}] {} phase: {} -> {}",
                    timestamp, contract_name, old_phase, new_phase
                )
            }
            Self::ContractStale {
                contract_name,
                last_activity,
                timestamp,
                ..
            } => {
                format!(
                    "[{}] {} is STALE (last activity: {})",
                    timestamp, contract_name, last_activity
                )
            }
            Self::Error { message, timestamp } => {
                format!("[{}] ERROR: {}", timestamp, message)
            }
        }
    }

    /// Format event for JSON output.
    pub fn to_json(&self) -> String {
        serde_json::to_string(self).unwrap_or_else(|_| "{}".to_string())
    }
}

/// Get current timestamp in ISO format.
pub fn now_timestamp() -> String {
    chrono::Utc::now().to_rfc3339()
}
