//! 邮箱级 access_token 鉴权中间件。
//!
//! 从 `Authorization: Bearer <token>` 或 `?token=` 提取 mailbox token，
//! 解析出 mailbox_id + secret，查库比对 hash，校验状态/有效期，
//! 通过后注入 [`MailboxContext`]。

use crate::{
    error::ErrorResponse, repositories::mailbox_repository::MailboxRepository,
    utils::mailbox_token, AppState,
};
use async_trait::async_trait;
use axum::extract::{FromRequestParts, Request, State};
use axum::http::request::Parts;
use axum::middleware::Next;
use axum::response::Response;

/// 当前请求解析出的邮箱上下文。
#[derive(Debug, Clone)]
pub struct MailboxContext {
    pub id: i64,
    pub address: String,
    #[allow(dead_code)]
    pub user_id: String,
}

#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for MailboxContext {
    type Rejection = ErrorResponse;
    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
        parts
            .extensions
            .get::<MailboxContext>()
            .cloned()
            .ok_or_else(|| ErrorResponse::unauthorized("mailbox context not found"))
    }
}

pub async fn mailbox_auth_middleware(
    State(state): State<AppState>,
    mut req: Request,
    next: Next,
) -> Result<Response, ErrorResponse> {
    let token =
        extract_token(&req).ok_or_else(|| ErrorResponse::unauthorized("missing mailbox token"))?;
    let (mailbox_id, secret) = mailbox_token::parse_token(&token)
        .ok_or_else(|| ErrorResponse::unauthorized("invalid mailbox token"))?;

    let m = MailboxRepository::new(state.pool.clone())
        .find_by_id(mailbox_id)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?
        .ok_or_else(|| ErrorResponse::unauthorized("mailbox not found"))?;

    if m.status != "active" {
        return Err(ErrorResponse::unauthorized("mailbox revoked"));
    }
    if chrono::Utc::now().naive_utc() > m.expires_at {
        return Err(ErrorResponse::unauthorized("mailbox expired"));
    }
    if m.access_token_hash != mailbox_token::hash_token(&secret) {
        return Err(ErrorResponse::unauthorized("invalid mailbox token"));
    }

    req.extensions_mut().insert(MailboxContext {
        id: m.id,
        address: m.address,
        user_id: m.user_id,
    });
    Ok(next.run(req).await)
}

fn extract_token(req: &Request) -> Option<String> {
    if let Some(h) = req
        .headers()
        .get(axum::http::header::AUTHORIZATION)
        .and_then(|v| v.to_str().ok())
    {
        if let Some(t) = h.strip_prefix("Bearer ") {
            if !t.is_empty() {
                return Some(t.to_string());
            }
        }
    }
    req.uri().query().and_then(|q| {
        q.split('&').find_map(|kv| {
            let (k, v) = kv.split_once('=')?;
            if k == "token" {
                Some(v.to_string())
            } else {
                None
            }
        })
    })
}
