use axum::{
    Json,
    extract::{Path, State},
};
use serde::Deserialize;
use serde_json::{Value, json};

use crate::{
    error::ApiError,
    service::{
        AgentBindingPayload, AgentPayload, PlatformApiKeyPayload, PublishPayload,
        UpdateAgentPayload,
    },
    state::AppState,
};

#[derive(Deserialize)]
pub struct WorkspacePath {
    pub workspace_id: String,
}

#[derive(Deserialize)]
pub struct WorkspaceAgentPath {
    pub workspace_id: String,
    pub agent_id: String,
}

#[derive(Deserialize)]
pub struct WorkspaceAgentVersionPath {
    pub workspace_id: String,
    pub agent_id: String,
    pub version: u32,
}

#[derive(Deserialize)]
pub struct WorkspaceAgentPlatformApiKeyPath {
    pub workspace_id: String,
    pub agent_id: String,
    pub key_id: String,
}

pub async fn list_agents(
    Path(path): Path<WorkspacePath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let items = state
        .service
        .list_agents(&path.workspace_id.as_str().into())
        .await?;
    Ok(Json(json!({ "items": items })))
}

pub async fn create_agent(
    Path(path): Path<WorkspacePath>,
    State(state): State<AppState>,
    Json(payload): Json<AgentPayload>,
) -> Result<Json<Value>, ApiError> {
    let created = state
        .service
        .create_agent(&path.workspace_id.as_str().into(), payload)
        .await?;
    Ok(Json(json!(created)))
}

pub async fn get_agent(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let agent = state
        .service
        .get_agent(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
        )
        .await?;
    Ok(Json(json!(agent)))
}

pub async fn update_agent(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
    Json(payload): Json<UpdateAgentPayload>,
) -> Result<Json<Value>, ApiError> {
    let updated = state
        .service
        .update_agent(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            payload,
        )
        .await?;
    Ok(Json(json!(updated)))
}

pub async fn delete_agent(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let deleted = state
        .service
        .delete_agent(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
        )
        .await?;
    Ok(Json(json!(deleted)))
}

pub async fn get_agent_version(
    Path(path): Path<WorkspaceAgentVersionPath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let version = state
        .service
        .get_agent_version(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            path.version,
        )
        .await?;
    Ok(Json(json!(version)))
}

pub async fn save_agent_bindings(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
    Json(payload): Json<Vec<AgentBindingPayload>>,
) -> Result<Json<Value>, ApiError> {
    let record = state
        .service
        .save_agent_bindings(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            payload,
        )
        .await?;
    Ok(Json(json!(record)))
}

pub async fn publish_agent(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
    Json(payload): Json<PublishPayload>,
) -> Result<Json<Value>, ApiError> {
    let published = state
        .service
        .publish_agent(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            payload.version,
        )
        .await?;
    Ok(Json(json!(published)))
}

pub async fn unpublish_agent(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let updated = state
        .service
        .unpublish_agent(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
        )
        .await?;
    Ok(Json(json!(updated)))
}

pub async fn archive_agent(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let updated = state
        .service
        .archive_agent(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
        )
        .await?;
    Ok(Json(json!(updated)))
}

pub async fn list_agent_platform_api_keys(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
) -> Result<Json<Value>, ApiError> {
    let items = state
        .service
        .list_agent_platform_api_keys(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
        )
        .await?;
    Ok(Json(json!({ "items": items })))
}

pub async fn create_agent_platform_api_key(
    Path(path): Path<WorkspaceAgentPath>,
    State(state): State<AppState>,
    Json(payload): Json<PlatformApiKeyPayload>,
) -> Result<Json<Value>, ApiError> {
    let created = state
        .service
        .create_agent_platform_api_key(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            payload,
        )
        .await?;
    Ok(Json(json!(created)))
}

pub async fn revoke_agent_platform_api_key(
    Path(path): Path<WorkspaceAgentPlatformApiKeyPath>,
    State(state): State<AppState>,
) -> Result<axum::http::StatusCode, ApiError> {
    state
        .service
        .revoke_agent_platform_api_key(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            &path.key_id.as_str().into(),
        )
        .await?;
    Ok(axum::http::StatusCode::NO_CONTENT)
}

pub async fn delete_agent_platform_api_key(
    Path(path): Path<WorkspaceAgentPlatformApiKeyPath>,
    State(state): State<AppState>,
) -> Result<axum::http::StatusCode, ApiError> {
    state
        .service
        .delete_agent_platform_api_key(
            &path.workspace_id.as_str().into(),
            &path.agent_id.as_str().into(),
            &path.key_id.as_str().into(),
        )
        .await?;
    Ok(axum::http::StatusCode::NO_CONTENT)
}
