use std::sync::Arc;
use std::time::Duration;

use rig::completion::ToolDefinition;
use rig::tool::Tool;
use serde::Deserialize;
use serde_json::json;
use tauri::Runtime;
use tokio::time::timeout;

use crate::agent::artifact::{ArtifactKind, ArtifactStore};
use crate::agent::stream_events::AgentStreamEmitter;
use crate::error::AppError;
use crate::state::PendingAgentToolApprovals;

const APPROVAL_TIMEOUT: Duration = Duration::from_secs(60);

#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ArtifactAction {
    Save,
    Load,
    List,
    Delete,
}

#[derive(Debug, Deserialize)]
pub struct ArtifactToolArgs {
    pub action: ArtifactAction,
    pub kind: String,
    pub name: Option<String>,
    pub content: Option<String>,
}

#[derive(Debug, thiserror::Error)]
pub enum ArtifactToolError {
    #[error("{0}")]
    App(#[from] AppError),
    #[error("artifact error: {0}")]
    Artifact(#[from] crate::agent::artifact::ArtifactError),
    #[error("failed to serialize tool result: {0}")]
    Serialize(String),
    #[error("{0}")]
    Validation(String),
}

pub struct ManageArtifactTool<R: Runtime> {
    pub store: Arc<ArtifactStore>,
    pub stream_emitter: Arc<AgentStreamEmitter<R>>,
    pub pending_approvals: Arc<PendingAgentToolApprovals>,
    pub request_id: String,
    pub _workspace_id: String,
}

impl<R: Runtime> Tool for ManageArtifactTool<R> {
    const NAME: &'static str = "manage_artifact";

    type Error = ArtifactToolError;
    type Args = ArtifactToolArgs;
    type Output = String;

    async fn definition(&self, _prompt: String) -> ToolDefinition {
        ToolDefinition {
            name: Self::NAME.to_string(),
            description: "Create, read, list, or delete app-managed artifacts (notes, reports, exports). Save and delete actions require user approval. All writes are constrained to the app artifacts directory.".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "action": {
                        "type": "string",
                        "enum": ["save", "load", "list", "delete"],
                        "description": "The artifact operation to perform."
                    },
                    "kind": {
                        "type": "string",
                        "enum": ["note", "report", "export"],
                        "description": "The artifact category."
                    },
                    "name": {
                        "type": "string",
                        "description": "File name for the artifact (required for save, load, delete). Example: 'thesis-AAPL.md'"
                    },
                    "content": {
                        "type": "string",
                        "description": "Text content to write (required for save)."
                    }
                },
                "required": ["action", "kind"],
                "additionalProperties": false
            }),
        }
    }

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        let kind = parse_kind(&args.kind)?;

        match args.action {
            ArtifactAction::Save => {
                let name = args.name.ok_or_else(|| {
                    ArtifactToolError::Validation("name is required for save".into())
                })?;
                let content = args.content.ok_or_else(|| {
                    ArtifactToolError::Validation("content is required for save".into())
                })?;

                self.stream_emitter
                    .tool_command(format!("artifact save {} {}", kind.subdir(), name))
                    .map_err(ArtifactToolError::App)?;

                let approved = self.await_approval(&kind, &name, "write").await?;
                if !approved {
                    return serialize_result(&json!({
                        "action": "save",
                        "kind": args.kind,
                        "name": name,
                        "status": "denied",
                        "summary": "The user denied approval for this artifact write."
                    }));
                }

                let meta = tokio::task::spawn_blocking({
                    let store = self.store.clone();
                    move || store.save(kind, &name, &content)
                })
                .await
                .map_err(|e| ArtifactToolError::Validation(e.to_string()))??;

                serialize_result(&json!({
                    "action": "save",
                    "kind": args.kind,
                    "name": meta.name,
                    "status": "saved",
                    "sizeBytes": meta.size_bytes,
                    "updatedAt": meta.updated_at,
                }))
            }
            ArtifactAction::Load => {
                let name = args.name.ok_or_else(|| {
                    ArtifactToolError::Validation("name is required for load".into())
                })?;

                self.stream_emitter
                    .tool_command(format!("artifact load {} {}", kind.subdir(), name))
                    .map_err(ArtifactToolError::App)?;

                let entry = tokio::task::spawn_blocking({
                    let store = self.store.clone();
                    move || store.load(kind, &name)
                })
                .await
                .map_err(|e| ArtifactToolError::Validation(e.to_string()))??;

                serialize_result(&json!({
                    "action": "load",
                    "kind": args.kind,
                    "name": entry.metadata.name,
                    "status": "loaded",
                    "content": entry.content,
                    "sizeBytes": entry.metadata.size_bytes,
                }))
            }
            ArtifactAction::List => {
                self.stream_emitter
                    .tool_command(format!("artifact list {}", kind.subdir()))
                    .map_err(ArtifactToolError::App)?;

                let entries = tokio::task::spawn_blocking({
                    let store = self.store.clone();
                    move || store.list(kind)
                })
                .await
                .map_err(|e| ArtifactToolError::Validation(e.to_string()))??;

                let items: Vec<serde_json::Value> = entries
                    .iter()
                    .map(|m| {
                        json!({
                            "name": m.name,
                            "sizeBytes": m.size_bytes,
                            "createdAt": m.created_at,
                            "updatedAt": m.updated_at,
                        })
                    })
                    .collect();

                serialize_result(&json!({
                    "action": "list",
                    "kind": args.kind,
                    "status": "listed",
                    "count": items.len(),
                    "artifacts": items,
                }))
            }
            ArtifactAction::Delete => {
                let name = args.name.ok_or_else(|| {
                    ArtifactToolError::Validation("name is required for delete".into())
                })?;

                self.stream_emitter
                    .tool_command(format!("artifact delete {} {}", kind.subdir(), name))
                    .map_err(ArtifactToolError::App)?;

                let approved = self.await_approval(&kind, &name, "delete").await?;
                if !approved {
                    return serialize_result(&json!({
                        "action": "delete",
                        "kind": args.kind,
                        "name": name,
                        "status": "denied",
                        "summary": "The user denied approval for this artifact deletion."
                    }));
                }

                let name_for_json = name.clone();
                tokio::task::spawn_blocking({
                    let store = self.store.clone();
                    move || store.delete(kind, &name)
                })
                .await
                .map_err(|e| ArtifactToolError::Validation(e.to_string()))??;

                serialize_result(&json!({
                    "action": "delete",
                    "kind": args.kind,
                    "name": name_for_json,
                    "status": "deleted",
                }))
            }
        }
    }
}

impl<R: Runtime> ManageArtifactTool<R> {
    async fn await_approval(
        &self,
        kind: &ArtifactKind,
        name: &str,
        action_label: &str,
    ) -> Result<bool, ArtifactToolError> {
        let (approval_id, receiver) = self
            .pending_approvals
            .register_for_request(Some(self.request_id.clone()))
            .map_err(ArtifactToolError::App)?;

        let title = format!("Approve artifact {}", action_label);
        let message = format!(
            "The agent wants to {} a {} artifact named '{}'.\n\nApprove this action to continue.",
            action_label,
            kind.subdir(),
            name,
        );

        self.stream_emitter
            .approval_required(
                approval_id.clone(),
                format!("artifact {}", action_label),
                title,
                message,
            )
            .map_err(ArtifactToolError::App)?;

        let result = timeout(APPROVAL_TIMEOUT, receiver).await;
        match result {
            Ok(Ok(approved)) => Ok(approved),
            Ok(Err(_)) => Ok(false),
            Err(_) => {
                self.pending_approvals.cancel(&approval_id);
                Ok(false)
            }
        }
    }
}

fn parse_kind(raw: &str) -> Result<ArtifactKind, ArtifactToolError> {
    match raw.to_ascii_lowercase().as_str() {
        "note" => Ok(ArtifactKind::Note),
        "report" => Ok(ArtifactKind::Report),
        "export" => Ok(ArtifactKind::Export),
        other => Err(ArtifactToolError::Validation(format!(
            "unknown artifact kind '{}'. Use note, report, or export.",
            other
        ))),
    }
}

fn serialize_result(value: &serde_json::Value) -> Result<String, ArtifactToolError> {
    serde_json::to_string(value).map_err(|error| ArtifactToolError::Serialize(error.to_string()))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::agent::artifact::ArtifactKind;

    #[test]
    fn parse_kind_accepts_known_variants() {
        assert_eq!(parse_kind("note").unwrap(), ArtifactKind::Note);
        assert_eq!(parse_kind("Report").unwrap(), ArtifactKind::Report);
        assert_eq!(parse_kind("EXPORT").unwrap(), ArtifactKind::Export);
    }

    #[test]
    fn parse_kind_rejects_unknown() {
        assert!(parse_kind("calendar").is_err());
    }
}
