//! Device-groups page — list, create, delete, add/remove member.
//! Strategies and AB shares hang off device-group membership, so this is
//! the canonical place to manage who can see whose devices.

use super::shared::{html_escape, notice_html, require_admin};
use crate::api::error::ApiError;
use crate::api::middleware::AuthedUser;
use crate::api::state::AppState;
use axum::extract::{Extension, Form, Path};
use axum::response::Html;
use serde::Deserialize;
use std::fmt::Write as _;
use std::sync::Arc;

pub async fn index(
    Extension(state): Extension<Arc<AppState>>,
    admin: AuthedUser,
) -> Result<Html<String>, ApiError> {
    require_admin(&admin)?;
    Ok(Html(render_full(&state).await?))
}

#[derive(Debug, Deserialize)]
pub struct CreateForm {
    pub name: String,
}

pub async fn create(
    Extension(state): Extension<Arc<AppState>>,
    admin: AuthedUser,
    Form(form): Form<CreateForm>,
) -> Result<Html<String>, ApiError> {
    require_admin(&admin)?;
    if form.name.trim().is_empty() {
        return notice_then(&state, "error", "Name required").await;
    }
    state
        .db
        .device_group_create(form.name.trim())
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    notice_then(&state, "ok", &format!("Group '{}' created.", form.name)).await
}

pub async fn delete(
    Extension(state): Extension<Arc<AppState>>,
    admin: AuthedUser,
    Path(id): Path<i64>,
) -> Result<Html<String>, ApiError> {
    require_admin(&admin)?;
    let ok = state
        .db
        .device_group_delete(id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    notice_then(
        &state,
        if ok { "ok" } else { "error" },
        if ok { "Group deleted." } else { "Already gone." },
    )
    .await
}

#[derive(Debug, Deserialize)]
pub struct MemberForm {
    pub user_id: i64,
}

pub async fn add_member(
    Extension(state): Extension<Arc<AppState>>,
    admin: AuthedUser,
    Path(id): Path<i64>,
    Form(form): Form<MemberForm>,
) -> Result<Html<String>, ApiError> {
    require_admin(&admin)?;
    state
        .db
        .device_group_add_member(id, form.user_id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(Html(render_full(&state).await?))
}

pub async fn remove_member(
    Extension(state): Extension<Arc<AppState>>,
    admin: AuthedUser,
    Path((id, user_id)): Path<(i64, i64)>,
) -> Result<Html<String>, ApiError> {
    require_admin(&admin)?;
    state
        .db
        .device_group_remove_member(id, user_id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(Html(render_full(&state).await?))
}

// ---------- rendering ----------

async fn notice_then(
    state: &Arc<AppState>,
    kind: &str,
    msg: &str,
) -> Result<Html<String>, ApiError> {
    let mut html = notice_html(kind, msg);
    html.push_str(&render_full(state).await?);
    Ok(Html(html))
}

async fn render_full(state: &Arc<AppState>) -> Result<String, ApiError> {
    let groups = state
        .db
        .device_groups_list_all()
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    let (_, all_users) = state
        .db
        .users_list_all(0, 1000)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    let mut s = String::new();
    s.push_str(
        r##"<div id="groups-region" class="space-y-6">
  <header><h2 class="text-lg font-semibold">Device groups</h2></header>
  <section class="rounded-md border border-slate-800 bg-slate-900 p-4">
    <h3 class="text-sm font-semibold text-slate-300 mb-3">Create group</h3>
    <form class="flex gap-2 text-sm" hx-post="/admin/pages/groups/create" hx-target="#groups-region" hx-swap="outerHTML">
      <input name="name" placeholder="group name" required class="flex-1 bg-slate-800 border border-slate-700 rounded px-2 py-1.5"/>
      <button class="bg-sky-600 hover:bg-sky-500 rounded px-3 py-1.5 font-medium text-white">Create</button>
    </form>
  </section>
"##,
    );

    if groups.is_empty() {
        s.push_str(
            r##"<p class="text-slate-500 text-sm">No device groups yet.</p>"##,
        );
    }
    for g in &groups {
        let members = state
            .db
            .device_group_members(g.id)
            .await
            .map_err(|e| ApiError::Internal(e.to_string()))?;
        let _ = write!(
            s,
            r##"<section class="rounded-md border border-slate-800 bg-slate-900 p-4 space-y-3">
  <header class="flex items-center justify-between">
    <h3 class="font-semibold">{name}</h3>
    <button class="text-xs text-rose-400 hover:text-rose-300"
      hx-post="/admin/pages/groups/{id}/delete"
      hx-confirm="Delete group {name}? Members aren't deleted; just unassigned."
      hx-target="#groups-region" hx-swap="outerHTML">Delete group</button>
  </header>
  <ul class="text-sm divide-y divide-slate-800">"##,
            id = g.id,
            name = html_escape(&g.name)
        );
        if members.is_empty() {
            s.push_str(
                r##"<li class="py-2 text-slate-500 text-xs">No members yet.</li>"##,
            );
        }
        for u in &members {
            let _ = write!(
                s,
                r##"<li class="py-2 flex items-center justify-between">
  <span class="text-slate-200">{username}</span>
  <button class="text-xs text-slate-400 hover:text-rose-300"
    hx-post="/admin/pages/groups/{gid}/members/{uid}/remove"
    hx-target="#groups-region" hx-swap="outerHTML">Remove</button>
</li>"##,
                username = html_escape(&u.username),
                gid = g.id,
                uid = u.id
            );
        }
        s.push_str("</ul>");
        // Add-member form: dropdown of users not currently in the group.
        let in_group: std::collections::HashSet<i64> =
            members.iter().map(|u| u.id).collect();
        let candidates: Vec<_> =
            all_users.iter().filter(|u| !in_group.contains(&u.id)).collect();
        if !candidates.is_empty() {
            let _ = write!(
                s,
                r##"<form class="flex gap-2 text-sm pt-2 border-t border-slate-800"
  hx-post="/admin/pages/groups/{id}/members/add"
  hx-target="#groups-region" hx-swap="outerHTML">
  <select name="user_id" class="flex-1 bg-slate-800 border border-slate-700 rounded px-2 py-1.5">
"##,
                id = g.id
            );
            for u in &candidates {
                let _ = write!(
                    s,
                    r##"<option value="{uid}">{username}</option>"##,
                    uid = u.id,
                    username = html_escape(&u.username)
                );
            }
            s.push_str(
                r##"</select>
  <button class="bg-sky-700 hover:bg-sky-600 rounded px-3 py-1.5 text-xs">Add member</button>
</form>"##,
            );
        }
        s.push_str("</section>");
    }
    s.push_str("</div>");
    Ok(s)
}
