//! 登录相关端点。
//!
//! 当前覆盖:
//! - `LoginRefreshService`(`/weapi/login/token/refresh`):用 jar 里的 `MUSIC_U` 续签
//! - `LoginQRService`(`GetKey`/`CheckQR`):二维码登录

use color_eyre::eyre::eyre;
use serde_json::json;

/// 本模块内部统一的 result 别名,屏蔽 color-eyre 全名。
type Result<T> = color_eyre::Result<T>;

use crate::transport::client::{RequestSpec, Transport};
use crate::transport::headers::UaKind;
use crate::transport::url::Crypto;

#[derive(Debug, Clone)]
pub struct LoginQrCode {
    pub url: String,
    pub unikey: String,
}

pub async fn login_refresh(transport: &Transport) -> Result<()> {
    transport
        .request(RequestSpec {
            path: "/weapi/login/token/refresh",
            crypto: Crypto::Weapi,
            params: serde_json::Map::new(),
            ua: UaKind::Pc,
        })
        .await?;
    mineral_log::info!(target: "login", "login token refreshed");
    Ok(())
}

/// 调 `GetKey` 拿 unikey,拼出二维码 URL。
pub async fn login_qr_get_key(transport: &Transport) -> Result<LoginQrCode> {
    let mut p = serde_json::Map::new();
    p.insert("type".into(), json!("1"));
    p.insert("noCheckToken".into(), json!("true"));
    let v = transport
        .request(RequestSpec {
            path: "/weapi/login/qrcode/unikey",
            crypto: Crypto::Weapi,
            params: p,
            ua: UaKind::Pc,
        })
        .await?;
    let unikey = v
        .get("unikey")
        .and_then(|x| x.as_str())
        .ok_or_else(|| eyre!("qrcode/unikey response missing `unikey`"))?
        .to_owned();
    let chain_id = crate::device::generate_chain_id();
    let url = format!("http://music.163.com/login?codekey={unikey}&chainId={chain_id}",);
    mineral_log::debug!(target: "login", unikey = unikey.as_str(), "qr key obtained");
    Ok(LoginQrCode { url, unikey })
}

/// 调 `CheckQR` 轮询扫码状态。返回 code:801=等待扫码、802=待手机确认、
/// 803=登录成功(cookie 已写入 jar)、800=二维码失效。
pub async fn login_qr_check(transport: &Transport, unikey: &str) -> Result<i64> {
    let mut p = serde_json::Map::new();
    p.insert("type".into(), json!("1"));
    p.insert("noCheckToken".into(), json!("true"));
    p.insert("key".into(), json!(unikey));

    // 这个端点会用非 200 的 code 表达扫码状态(801/802/800),不能让 transport
    // 因为 code != 200 报错,所以走"宽松路径"——直接调底层。
    let result = transport
        .request_lax(RequestSpec {
            path: "/weapi/login/qrcode/client/login",
            crypto: Crypto::Weapi,
            params: p,
            ua: UaKind::Pc,
        })
        .await?;
    let code = result
        .get("code")
        .and_then(serde_json::Value::as_i64)
        .unwrap_or(0);
    // 801=等待扫码 802=待确认 803=成功 800=失效
    mineral_log::debug!(target: "login", code, "qr check status");
    Ok(code)
}
