use serde::{Deserialize, Serialize};

/// Supported external project-management systems.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ExternalSystem {
    Linear,
    Jira,
    Notion,
}

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

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "linear" => Ok(ExternalSystem::Linear),
            "jira" => Ok(ExternalSystem::Jira),
            "notion" => Ok(ExternalSystem::Notion),
            other => Err(format!("Unknown external system: '{}'. Expected: Linear, Jira, or Notion", other)),
        }
    }
}

impl std::fmt::Display for ExternalSystem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ExternalSystem::Linear => write!(f, "Linear"),
            ExternalSystem::Jira => write!(f, "Jira"),
            ExternalSystem::Notion => write!(f, "Notion"),
        }
    }
}

/// A reference linking a FURNACE scroll to an external system entity.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExternalRef {
    pub system: ExternalSystem,
    pub external_id: String,
    pub url: String,
    pub last_synced: i64,
    /// Direction of sync for this link.
    pub sync_direction: SyncDirection,
    /// Reference key for the credential in keyring (e.g. "furnace-sync:linear").
    pub token_ref: Option<String>,
}

/// Configuration for connecting to an external MCP server.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServerConfig {
    pub system: ExternalSystem,
    /// Command to spawn the MCP server process (e.g. "npx").
    pub command: String,
    /// Arguments to the command (e.g. ["-y", "@linear/mcp-server"]).
    pub args: Vec<String>,
    /// Environment variables to set (e.g. LINEAR_API_KEY).
    pub env_vars: Vec<(String, String)>,
}

/// Direction of a sync operation.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub enum SyncDirection {
    Push,
    Pull,
    #[default]
    Bidirectional,
}

impl std::fmt::Display for SyncDirection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SyncDirection::Push => write!(f, "Push"),
            SyncDirection::Pull => write!(f, "Pull"),
            SyncDirection::Bidirectional => write!(f, "Bidirectional"),
        }
    }
}

/// An event emitted when a scroll changes and needs to be synced.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScrollEvent {
    pub scroll_id: String,
    pub event_type: String,
    pub timestamp: i64,
}

/// Result of a sync operation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncResult {
    pub success: bool,
    pub external_id: Option<String>,
    pub message: String,
}
