//! 临时邮箱与邮件 HTTP 接口 + WebSocket 实时推送。
//!
//! - 邮箱管理（创建/列表/吊销/轮换）：用户 JWT
//! - 邮件查询（列表/详情/删除/附件/WS）：邮箱级 access_token（mailbox_auth_middleware）

use crate::{
    domain::{
        dto::mailbox::CreateMailboxRequest,
        vo::{
            email::{EmailDetail, EmailSummary},
            mailbox::{CreateMailboxResult, MailboxVO},
            ApiResponse,
        },
    },
    error::ErrorResponse,
    infra::middleware::{MailboxContext, UserId},
    services::{
        credit_rule_service::CreditRuleService, email_service::EmailService,
        mailbox_service::MailboxService,
    },
    smtp::MailHub,
    AppState,
};
use axum::{
    extract::{
        ws::{Message, WebSocket, WebSocketUpgrade},
        Path, Query, State,
    },
    http::{HeaderValue, StatusCode},
    response::{IntoResponse, Response},
    Json,
};
use serde::Deserialize;
use validator::Validate;

// ===== 邮箱管理（用户 JWT） =====

pub async fn create_mailbox(
    State(state): State<AppState>,
    UserId(user_id): UserId,
    Json(req): Json<CreateMailboxRequest>,
) -> Result<Json<ApiResponse<CreateMailboxResult>>, ErrorResponse> {
    req.validate()
        .map_err(|e| ErrorResponse::bad_request(e.to_string()))?;

    let svc = MailboxService::new(state.pool.clone(), state.config.smtp.clone());
    let rules = CreditRuleService::new(state.pool.clone())
        .get()
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    let result = svc
        .create_with_billing(&user_id, req, &rules)
        .await
        .map_err(|e| {
            let message = e.to_string();
            if message.contains("limit reached") || message.contains("积分不足") {
                ErrorResponse::too_many_requests(message)
            } else {
                ErrorResponse::bad_request(message)
            }
        })?;

    Ok(Json(ApiResponse::success(result)))
}

pub async fn list_mailboxes(
    State(state): State<AppState>,
    UserId(user_id): UserId,
) -> Result<Json<ApiResponse<Vec<MailboxVO>>>, ErrorResponse> {
    let svc = MailboxService::new(state.pool.clone(), state.config.smtp.clone());
    let list = svc
        .list_by_user(&user_id)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    Ok(Json(ApiResponse::success(list)))
}

pub async fn delete_mailbox(
    State(state): State<AppState>,
    UserId(user_id): UserId,
    Path(id): Path<i64>,
) -> Result<Json<ApiResponse<()>>, ErrorResponse> {
    let svc = MailboxService::new(state.pool.clone(), state.config.smtp.clone());
    svc.revoke(&user_id, id)
        .await
        .map_err(|e| ErrorResponse::bad_request(e.to_string()))?;
    Ok(Json(ApiResponse::success_with_message(
        (),
        "mailbox revoked",
    )))
}

pub async fn rotate_token(
    State(state): State<AppState>,
    UserId(user_id): UserId,
    Path(id): Path<i64>,
) -> Result<Json<ApiResponse<serde_json::Value>>, ErrorResponse> {
    let svc = MailboxService::new(state.pool.clone(), state.config.smtp.clone());
    let token = svc
        .rotate_token(&user_id, id)
        .await
        .map_err(|e| ErrorResponse::bad_request(e.to_string()))?;
    Ok(Json(ApiResponse::success(
        serde_json::json!({ "access_token": token }),
    )))
}

// ===== 邮件查询（邮箱 access_token） =====

#[derive(Deserialize)]
pub struct ListEmailsQuery {
    pub limit: Option<u64>,
    pub offset: Option<u64>,
}

pub async fn list_emails(
    State(state): State<AppState>,
    ctx: MailboxContext,
    Path(id): Path<i64>,
    Query(q): Query<ListEmailsQuery>,
) -> Result<Json<ApiResponse<Vec<EmailSummary>>>, ErrorResponse> {
    if ctx.id != id {
        return Err(ErrorResponse::unauthorized("mailbox mismatch"));
    }
    let limit = q.limit.unwrap_or(20).clamp(1, 100);
    let offset = q.offset.unwrap_or(0);
    let svc = EmailService::new(state.pool.clone(), state.config.smtp.clone());
    let list = svc
        .list_by_mailbox(id, limit, offset)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    Ok(Json(ApiResponse::success(list)))
}

pub async fn get_email(
    State(state): State<AppState>,
    ctx: MailboxContext,
    Path((id, eid)): Path<(i64, i64)>,
) -> Result<Json<ApiResponse<EmailDetail>>, ErrorResponse> {
    if ctx.id != id {
        return Err(ErrorResponse::unauthorized("mailbox mismatch"));
    }
    let svc = EmailService::new(state.pool.clone(), state.config.smtp.clone());
    let detail = svc
        .get_detail(id, eid)
        .await
        .map_err(|e| ErrorResponse::not_found(e.to_string()))?;
    Ok(Json(ApiResponse::success(detail)))
}

pub async fn delete_email(
    State(state): State<AppState>,
    ctx: MailboxContext,
    Path((id, eid)): Path<(i64, i64)>,
) -> Result<Json<ApiResponse<()>>, ErrorResponse> {
    if ctx.id != id {
        return Err(ErrorResponse::unauthorized("mailbox mismatch"));
    }
    let svc = EmailService::new(state.pool.clone(), state.config.smtp.clone());
    svc.delete(id, eid)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    Ok(Json(ApiResponse::success_with_message((), "email deleted")))
}

pub async fn get_attachment(
    State(state): State<AppState>,
    ctx: MailboxContext,
    Path((id, eid, aid)): Path<(i64, i64, i64)>,
) -> Result<Response, ErrorResponse> {
    if ctx.id != id {
        return Err(ErrorResponse::unauthorized("mailbox mismatch"));
    }
    let svc = EmailService::new(state.pool.clone(), state.config.smtp.clone());
    let (filename, content_type, content) = svc
        .get_attachment(id, eid, aid)
        .await
        .map_err(|e| ErrorResponse::not_found(e.to_string()))?;
    let mut resp = (StatusCode::OK, content).into_response();
    let headers = resp.headers_mut();
    headers.insert(
        axum::http::header::CONTENT_TYPE,
        content_type
            .parse()
            .unwrap_or_else(|_| HeaderValue::from_static("application/octet-stream")),
    );
    let cd = format!(
        "attachment; filename=\"{}\"",
        filename.unwrap_or_else(|| "attachment".into())
    );
    if let Ok(v) = cd.parse() {
        headers.insert(axum::http::header::CONTENT_DISPOSITION, v);
    }
    Ok(resp)
}

// ===== WebSocket 实时推送（邮箱 access_token，?token=） =====

pub async fn ws_subscribe(
    State(state): State<AppState>,
    ctx: MailboxContext,
    Path(id): Path<i64>,
    ws: WebSocketUpgrade,
) -> Response {
    if ctx.id != id {
        return ErrorResponse::unauthorized("mailbox mismatch").into_response();
    }
    let hub = state.mail_hub.clone();
    ws.on_upgrade(move |socket| handle_ws(socket, hub, ctx.address))
}

async fn handle_ws(mut socket: WebSocket, hub: MailHub, address: String) {
    let mut rx = hub.subscribe(&address);
    loop {
        match rx.recv().await {
            Ok(payload) => {
                if socket.send(Message::Text(payload)).await.is_err() {
                    break;
                }
            }
            Err(tokio::sync::broadcast::error::RecvError::Closed) => break,
            Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => continue,
        }
    }
    hub.cleanup(&address);
}
