//! Naked `kranth` entrypoint — auth gate + hand-off to the ratatui cinema.
//!
//! Slash commands inside the shell are owned by the cinema itself (it's a
//! single ratatui app, not a hybrid rustyline/ratatui split). This module
//! only verifies the user is signed in, resolves their display label from
//! the live API, and transitions execution into `cinema::run`.

use std::io::{self, Write};
use std::time::Duration;

use anyhow::Context;
use crossterm::tty::IsTty;
use serde::Deserialize;

use crate::auth;
use crate::cinema;

pub async fn run(api_url: &str, api_key_override: Option<&str>) -> anyhow::Result<()> {
    if !io::stdout().is_tty() {
        anyhow::bail!(
            "kranth: the interactive shell needs a terminal. Use `kranth run \"<idea>\"` for piped / scripted usage."
        );
    }

    let api_key = match auth::resolve_key(api_url, api_key_override)? {
        Some(k) => Some(k),
        None => {
            let _ = writeln!(
                io::stdout(),
                "\nkranth: not signed in.\n  Run `kranth login` first, or export KRANTH_API_KEY=kr_live_…\n"
            );
            return Ok(());
        }
    };

    let label = account_label(api_url, api_key.as_deref()).await;
    let key = api_key.unwrap_or_default();
    cinema::run(api_url, &key, &label).context("ratatui cinema")
}

#[derive(Deserialize)]
struct MeResponse {
    #[serde(default)]
    email: Option<String>,
}

#[derive(Deserialize)]
struct UsageResponse {
    #[serde(default)]
    plan: Option<String>,
    #[serde(default)]
    credits_remaining: Option<i64>,
}

/// Build the splash subtitle from live `/v1/me` + `/v1/usage` responses.
/// Either call failing falls back gracefully so we never block the cinema
/// boot — worst-case label is the literal "signed in" we shipped before.
async fn account_label(api_url: &str, api_key: Option<&str>) -> String {
    let Some(key) = api_key else {
        return "signed in".to_string();
    };
    let Ok(http) = reqwest::Client::builder()
        .timeout(Duration::from_secs(4))
        .build()
    else {
        return "signed in".to_string();
    };

    let me = fetch_json::<MeResponse>(&http, &format!("{api_url}/v1/me"), key).await;
    let usage = fetch_json::<UsageResponse>(&http, &format!("{api_url}/v1/usage"), key).await;

    let email = me.and_then(|m| m.email).unwrap_or_else(|| "signed in".to_string());
    let plan = usage.as_ref().and_then(|u| u.plan.as_deref()).unwrap_or("free");
    let credits = usage.as_ref().and_then(|u| u.credits_remaining).unwrap_or(0);
    format!("{email}  ·  {credits} credits  ·  {plan}")
}

async fn fetch_json<T: serde::de::DeserializeOwned>(
    http: &reqwest::Client,
    url: &str,
    key: &str,
) -> Option<T> {
    let resp = http.get(url).bearer_auth(key).send().await.ok()?;
    if !resp.status().is_success() {
        return None;
    }
    resp.json::<T>().await.ok()
}
