use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt;
use tokio::sync::mpsc;

use crate::error::SwarmResult;
use crate::message::Message;

#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct AgentId(pub String);

impl AgentId {
    pub fn new(name: impl Into<String>) -> Self {
        Self(name.into())
    }

    pub fn system() -> Self {
        Self("system".to_string())
    }
}

impl fmt::Display for AgentId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum AgentRole {
    Orchestrator,
    Planner,
    Developer,
    Reviewer,
    Tester,
    Medic,
    Strategist,
    Social,
    Custom(String),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum AgentStatus {
    Idle,
    Thinking,
    Executing,
    WaitingForTool,
    Error(String),
    Stopped,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentManifest {
    pub id: AgentId,
    pub name: String,
    pub role: AgentRole,
    pub system_prompt: String,
    pub llm_provider: String,
    pub model: String,
    pub fallback_chain: Vec<(String, String)>,
    pub tools: Vec<String>,
    pub max_turns: usize,
    pub temperature: f32,
}

impl Default for AgentManifest {
    fn default() -> Self {
        Self {
            id: AgentId::new("default"),
            name: "Default Agent".to_string(),
            role: AgentRole::Custom("general".to_string()),
            system_prompt: String::new(),
            llm_provider: "anthropic".to_string(),
            model: "claude-sonnet-4-20250514".to_string(),
            fallback_chain: vec![],
            tools: vec![],
            max_turns: 20,
            temperature: 0.7,
        }
    }
}

#[async_trait]
pub trait Agent: Send + Sync {
    fn id(&self) -> &AgentId;
    fn manifest(&self) -> &AgentManifest;
    fn status(&self) -> &AgentStatus;

    async fn process(&mut self, message: Message) -> SwarmResult<Message>;
    async fn start(&mut self) -> SwarmResult<()>;
    async fn stop(&mut self) -> SwarmResult<()>;
}

pub struct AgentHandle {
    pub id: AgentId,
    pub manifest: AgentManifest,
    pub status: AgentStatus,
    pub tx: mpsc::Sender<Message>,
    pub started_at: DateTime<Utc>,
}

impl AgentHandle {
    pub fn new(manifest: AgentManifest, tx: mpsc::Sender<Message>) -> Self {
        Self {
            id: manifest.id.clone(),
            manifest,
            status: AgentStatus::Idle,
            tx,
            started_at: Utc::now(),
        }
    }

    pub async fn send(&self, msg: Message) -> SwarmResult<()> {
        self.tx
            .send(msg)
            .await
            .map_err(|_| crate::error::SwarmError::ChannelClosed)
    }
}
