//! `swarmhive releases {list,get,create,update,publish,yank}` — 管理应用的版本。
//!
//! 注意:`releases publish` 是「发布一个已存在的 draft」;`publish {tauri|android}` 是
//! 「扫 bundle → 上传 → complete」的上传式发布,两者不同。

use std::path::PathBuf;

use anyhow::Result;
use swarmhive_api_types::{CreateReleaseRequest, Release, UpdateReleaseRequest};
use tabled::Tabled;

use crate::commands::client::{
    OutputFormat, emit, emit_one, get_json, patch_json, post_empty_json, post_json, read_opt_file,
    require_creds,
};

#[derive(Tabled)]
pub(crate) struct ReleaseRow {
    version: String,
    status: String,
    #[tabled(rename = "android code")]
    android_version_code: String,
    #[tabled(rename = "rollout")]
    rollout: String,
    #[tabled(rename = "min ver")]
    min_version: String,
    #[tabled(rename = "published")]
    published_at: String,
}

pub(crate) fn release_row(r: &Release) -> ReleaseRow {
    ReleaseRow {
        version: r.version.clone(),
        status: format!("{:?}", r.status).to_lowercase(),
        android_version_code: r
            .android_version_code
            .map(|c| c.to_string())
            .unwrap_or_default(),
        // <100 显示灰度百分比;None / 100 视作全量。
        rollout: r
            .rollout_percent
            .filter(|p| *p < 100)
            .map(|p| format!("{p}%"))
            .unwrap_or_else(|| "full".to_string()),
        // 去掉 "0.0.0" sentinel(=无下限)。
        min_version: r
            .min_version
            .as_deref()
            .filter(|v| *v != "0.0.0")
            .unwrap_or("-")
            .to_string(),
        published_at: r.published_at.map(|t| t.to_rfc3339()).unwrap_or_default(),
    }
}

pub async fn list(app: &str, output: OutputFormat) -> Result<()> {
    let creds = require_creds()?;
    let releases: Vec<Release> = get_json(&creds, &format!("/api/v1/apps/{app}/releases")).await?;
    emit(&releases, output, release_row)
}

pub async fn get(app: &str, version: &str, output: OutputFormat) -> Result<()> {
    let creds = require_creds()?;
    let release: Release =
        get_json(&creds, &format!("/api/v1/apps/{app}/releases/{version}")).await?;
    emit_one(&release, output, release_row)
}

pub async fn create(
    app: &str,
    version: String,
    android_version_code: Option<i64>,
    android_min_version_code: Option<i64>,
    notes_file: Option<PathBuf>,
    output: OutputFormat,
) -> Result<()> {
    let creds = require_creds()?;
    let body = CreateReleaseRequest {
        version,
        android_version_code,
        android_min_version_code,
        release_notes: read_opt_file(notes_file)?,
    };
    let client = reqwest::Client::new();
    let created: Release = post_json(
        &client,
        &creds,
        &format!("/api/v1/apps/{app}/releases"),
        &body,
    )
    .await?;
    emit_one(&created, output, release_row)
}

#[allow(clippy::too_many_arguments)]
pub async fn update(
    app: &str,
    version: &str,
    android_version_code: Option<i64>,
    rollout_percent: Option<i16>,
    min_version: Option<String>,
    android_min_version_code: Option<i64>,
    notes_file: Option<PathBuf>,
    output: OutputFormat,
) -> Result<()> {
    let creds = require_creds()?;
    // flag 直接映射 Option:省略=不改;传值=设;清灰度传 --rollout-percent 100、
    // 清 Tauri 强更下限传 --min-version 0.0.0(后端单层 Option sentinel,与 UI 一致)。
    let body = UpdateReleaseRequest {
        android_version_code,
        android_min_version_code,
        release_notes: read_opt_file(notes_file)?,
        min_version,
        rollout_percent,
    };
    let updated: Release = patch_json(
        &creds,
        &format!("/api/v1/apps/{app}/releases/{version}"),
        &body,
    )
    .await?;
    emit_one(&updated, output, release_row)
}

pub async fn publish(app: &str, version: &str, output: OutputFormat) -> Result<()> {
    let creds = require_creds()?;
    let released: Release = post_empty_json(
        &creds,
        &format!("/api/v1/apps/{app}/releases/{version}/publish"),
    )
    .await?;
    emit_one(&released, output, release_row)
}

/// `releases finalize` —— 把上传到 draft 的 release 显式发布(幂等)。多 target 发布的
/// 推荐收尾:N 个 target 各自 `publish`(到 draft)后,末步一次 finalize。已 published
/// 再调返回 200 不报错。
pub async fn finalize(app: &str, version: &str, output: OutputFormat) -> Result<()> {
    let creds = require_creds()?;
    let released: Release = post_empty_json(
        &creds,
        &format!("/api/v1/apps/{app}/releases/{version}/finalize"),
    )
    .await?;
    emit_one(&released, output, release_row)
}

pub async fn yank(app: &str, version: &str, yes: bool, output: OutputFormat) -> Result<()> {
    anyhow::ensure!(yes, "refusing to yank release '{version}' without --yes");
    let creds = require_creds()?;
    let yanked: Release = post_empty_json(
        &creds,
        &format!("/api/v1/apps/{app}/releases/{version}/yank"),
    )
    .await?;
    emit_one(&yanked, output, release_row)
}
