//! Release management database operations

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use uuid::Uuid;

/// Release record from database
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Release {
    pub id: Uuid,
    pub version: String,
    pub download_url: String,
    pub checksum_sha256: String,
    pub release_notes: Option<String>,
    pub is_stable: bool,
    pub is_mandatory: bool,
    pub min_version: Option<String>,
    pub created_at: DateTime<Utc>,
}

/// Create a new release
#[allow(clippy::too_many_arguments)] // signature mirrors the relay/session protocol contract; refactor into a params struct tracked in docs/specs/native-remote-control/
pub async fn create_release(
    pool: &PgPool,
    version: &str,
    download_url: &str,
    checksum_sha256: &str,
    release_notes: Option<&str>,
    is_stable: bool,
    is_mandatory: bool,
    min_version: Option<&str>,
) -> Result<Release, sqlx::Error> {
    sqlx::query_as::<_, Release>(
        r#"
        INSERT INTO releases (version, download_url, checksum_sha256, release_notes, is_stable, is_mandatory, min_version)
        VALUES ($1, $2, $3, $4, $5, $6, $7)
        RETURNING *
        "#,
    )
    .bind(version)
    .bind(download_url)
    .bind(checksum_sha256)
    .bind(release_notes)
    .bind(is_stable)
    .bind(is_mandatory)
    .bind(min_version)
    .fetch_one(pool)
    .await
}

/// Get the latest stable release
pub async fn get_latest_stable_release(pool: &PgPool) -> Result<Option<Release>, sqlx::Error> {
    sqlx::query_as::<_, Release>(
        r#"
        SELECT * FROM releases
        WHERE is_stable = true
        ORDER BY created_at DESC
        LIMIT 1
        "#,
    )
    .fetch_optional(pool)
    .await
}

/// Get a release by version
pub async fn get_release_by_version(
    pool: &PgPool,
    version: &str,
) -> Result<Option<Release>, sqlx::Error> {
    sqlx::query_as::<_, Release>("SELECT * FROM releases WHERE version = $1")
        .bind(version)
        .fetch_optional(pool)
        .await
}

/// Get all releases (ordered by creation date, newest first)
pub async fn get_all_releases(pool: &PgPool) -> Result<Vec<Release>, sqlx::Error> {
    sqlx::query_as::<_, Release>("SELECT * FROM releases ORDER BY created_at DESC")
        .fetch_all(pool)
        .await
}

/// Update a release
pub async fn update_release(
    pool: &PgPool,
    version: &str,
    release_notes: Option<&str>,
    is_stable: bool,
    is_mandatory: bool,
) -> Result<Option<Release>, sqlx::Error> {
    sqlx::query_as::<_, Release>(
        r#"
        UPDATE releases SET
            release_notes = COALESCE($2, release_notes),
            is_stable = $3,
            is_mandatory = $4
        WHERE version = $1
        RETURNING *
        "#,
    )
    .bind(version)
    .bind(release_notes)
    .bind(is_stable)
    .bind(is_mandatory)
    .fetch_optional(pool)
    .await
}

/// Delete a release
pub async fn delete_release(pool: &PgPool, version: &str) -> Result<bool, sqlx::Error> {
    let result = sqlx::query("DELETE FROM releases WHERE version = $1")
        .bind(version)
        .execute(pool)
        .await?;
    Ok(result.rows_affected() > 0)
}

/// Update machine version info
pub async fn update_machine_version(
    pool: &PgPool,
    agent_id: &str,
    agent_version: &str,
) -> Result<(), sqlx::Error> {
    sqlx::query(
        r#"
        UPDATE connect_machines SET
            agent_version = $1,
            last_update_check = NOW()
        WHERE agent_id = $2
        "#,
    )
    .bind(agent_version)
    .bind(agent_id)
    .execute(pool)
    .await?;
    Ok(())
}

/// Update machine update status
pub async fn update_machine_update_status(
    pool: &PgPool,
    agent_id: &str,
    update_status: &str,
) -> Result<(), sqlx::Error> {
    sqlx::query(
        r#"
        UPDATE connect_machines SET
            update_status = $1
        WHERE agent_id = $2
        "#,
    )
    .bind(update_status)
    .bind(agent_id)
    .execute(pool)
    .await?;
    Ok(())
}

/// Get machines that need updates (version < latest stable)
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
pub async fn get_machines_needing_update(
    pool: &PgPool,
    latest_version: &str,
) -> Result<Vec<String>, sqlx::Error> {
    // Note: This does simple string comparison which works for semver if formatted consistently
    // For production, you might want a more robust version comparison
    let rows: Vec<(String,)> = sqlx::query_as(
        r#"
        SELECT agent_id FROM connect_machines
        WHERE status = 'online'
          AND is_persistent = true
          AND (agent_version IS NULL OR agent_version < $1)
        "#,
    )
    .bind(latest_version)
    .fetch_all(pool)
    .await?;

    Ok(rows.into_iter().map(|(id,)| id).collect())
}
