use actix_web::{HttpResponse, web, web::ServiceConfig};
use service::AppService;
use service::agent::conversation::{
    ConversationResponse, ConversationWithSessionResponse, CreateConversation,
    MessageResponse, UpdateConversation,
};
use service::agent::types::{AgentRunRequest, AgentRunResponse};
use session::Session;
use tokio_stream::StreamExt;
use tokio_stream::wrappers::UnboundedReceiverStream;
use uuid::Uuid;

use crate::error::{ApiError, ok_json};

pub fn configure(cfg: &mut ServiceConfig) {
    cfg.service(
        web::resource("/sessions/{session_id}/conversations")
            .route(web::get().to(list_conversations))
            .route(web::post().to(create_conversation)),
    )
    .service(
        web::resource("/conversations")
            .route(web::get().to(list_all_conversations)),
    )
    .service(
        web::resource("/conversations/{id}")
            .route(web::get().to(get_conversation))
            .route(web::patch().to(update_conversation))
            .route(web::delete().to(delete_conversation)),
    )
    .service(
        web::resource("/conversations/{id}/messages")
            .route(web::get().to(list_messages))
            .route(web::post().to(send_message)),
    )
    .service(
        web::resource("/conversations/{id}/stream")
            .route(web::post().to(stream_agent)),
    )
    .service(
        web::resource("/conversations/{id}/fork")
            .route(web::post().to(fork_conversation)),
    );
}

#[utoipa::path(
    get, path = "/api/v1/agent/sessions/{session_id}/conversations",
    params(("session_id" = Uuid, Path)),
    responses((status = 200, body = Vec<ConversationResponse>)),
    security(("session" = []))
)]
pub async fn list_conversations(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_list(user_id, path.into_inner())
            .await?,
    )
}

#[utoipa::path(
    post, path = "/api/v1/agent/sessions/{session_id}/conversations",
    params(("session_id" = Uuid, Path)),
    request_body = CreateConversation,
    responses((status = 200, body = ConversationResponse)),
    security(("session" = []))
)]
pub async fn create_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
    body: web::Json<CreateConversation>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_create(
                user_id,
                path.into_inner(),
                body.into_inner(),
            )
            .await?,
    )
}

#[derive(Debug, serde::Deserialize, utoipa::IntoParams)]
pub struct ListAllConversationsQuery {
    pub wk: Option<String>,
}

#[utoipa::path(
    get, path = "/api/v1/agent/conversations",
    params(("wk" = Option<String>, Query, description = "Filter by workspace name")),
    responses((status = 200, body = Vec<ConversationWithSessionResponse>)),
    security(("session" = []))
)]
pub async fn list_all_conversations(
    session: Session,
    service: web::Data<AppService>,
    query: web::Query<ListAllConversationsQuery>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_list_all(user_id, query.wk.as_deref())
            .await?,
    )
}

#[utoipa::path(
    get, path = "/api/v1/agent/conversations/{id}",
    params(("id" = Uuid, Path)),
    responses((status = 200, body = ConversationResponse)),
    security(("session" = []))
)]
pub async fn get_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_get(user_id, path.into_inner())
            .await?,
    )
}

#[utoipa::path(
    patch, path = "/api/v1/agent/conversations/{id}",
    params(("id" = Uuid, Path)),
    request_body = UpdateConversation,
    responses((status = 200, body = ConversationResponse)),
    security(("session" = []))
)]
pub async fn update_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
    body: web::Json<UpdateConversation>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_update(
                user_id,
                path.into_inner(),
                body.into_inner(),
            )
            .await?,
    )
}

#[utoipa::path(
    delete, path = "/api/v1/agent/conversations/{id}",
    params(("id" = Uuid, Path)),
    responses((status = 200)),
    security(("session" = []))
)]
pub async fn delete_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    service
        .agent_conversation_delete(user_id, path.into_inner())
        .await?;
    Ok(HttpResponse::Ok().json(serde_json::json!({ "deleted": true })))
}

#[utoipa::path(
    post, path = "/api/v1/agent/conversations/{id}/archive",
    params(("id" = Uuid, Path)),
    responses((status = 200, body = ConversationResponse)),
    security(("session" = []))
)]
pub async fn archive_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_archive(user_id, path.into_inner())
            .await?,
    )
}

#[utoipa::path(
    post, path = "/api/v1/agent/conversations/{id}/unarchive",
    params(("id" = Uuid, Path)),
    responses((status = 200, body = ConversationResponse)),
    security(("session" = []))
)]
pub async fn unarchive_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_unarchive(user_id, path.into_inner())
            .await?,
    )
}
#[utoipa::path(
    get, path = "/api/v1/agent/conversations/{id}/messages",
    params(("id" = Uuid, Path), ("before" = Option<Uuid>, Query), ("limit" = Option<u32>, Query)),
    responses((status = 200, body = Vec<MessageResponse>)),
    security(("session" = []))
)]
pub async fn list_messages(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
    query: web::Query<MessageListQuery>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_message_list(
                user_id,
                path.into_inner(),
                query.limit,
                query.before,
            )
            .await?,
    )
}

#[derive(Debug, serde::Deserialize, utoipa::IntoParams)]
pub struct MessageListQuery {
    pub limit: Option<u32>,
    pub before: Option<Uuid>,
}
#[utoipa::path(
    post, path = "/api/v1/agent/conversations/{id}/messages",
    params(("id" = Uuid, Path)),
    request_body = AgentRunRequest,
    responses((status = 200, body = AgentRunResponse)),
    security(("session" = []))
)]
pub async fn send_message(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
    body: web::Json<AgentRunRequest>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    let conversation_id = path.into_inner();
    let mut req = body.into_inner();
    req.conversation_id = Some(conversation_id);
    ok_json(service.agent_run(user_id, req).await?)
}
#[utoipa::path(
    post, path = "/api/v1/agent/conversations/{id}/stream",
    params(("id" = Uuid, Path)),
    request_body = AgentRunRequest,
    responses((status = 200, description = "SSE stream")),
    security(("session" = []))
)]
pub async fn stream_agent(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
    body: web::Json<AgentRunRequest>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    let conversation_id = path.into_inner();
    let mut req = body.into_inner();
    req.conversation_id = Some(conversation_id);

    let rx = service.agent_run_streaming(user_id, req).await?;

    let stream = UnboundedReceiverStream::new(rx).map(|payload| {
        let frame = if payload.starts_with("data:") {
            payload
        } else {
            format!("data: {}\n\n", payload)
        };
        Ok::<_, actix_web::Error>(actix_web::web::Bytes::from(frame))
    });

    Ok(HttpResponse::Ok()
        .content_type("text/event-stream")
        .insert_header(("Cache-Control", "no-cache"))
        .insert_header(("Connection", "keep-alive"))
        .insert_header(("X-Accel-Buffering", "no"))
        .streaming(stream))
}

#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct ForkConversationRequest {
    pub message_id: Option<Uuid>,
    pub title: Option<String>,
}
#[utoipa::path(
    post, path = "/api/v1/agent/conversations/{id}/fork",
    params(("id" = Uuid, Path)),
    request_body = ForkConversationRequest,
    responses((status = 200, body = ConversationResponse)),
    security(("session" = []))
)]
pub async fn fork_conversation(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<Uuid>,
    body: web::Json<ForkConversationRequest>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_json(
        service
            .agent_conversation_fork(
                user_id,
                path.into_inner(),
                body.message_id,
                body.title.as_deref(),
            )
            .await?,
    )
}
