//! JWT Token Generation for FXT
//!
//! Provides JWT token generation for simulated authentication flows.
//! Uses the same configuration as the unified API for compatibility.

use anyhow::Result;
use jsonwebtoken::{encode, EncodingKey, Header};
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
use uuid::Uuid;

/// JWT claims structure matching API expectations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JwtClaims {
    /// JWT ID (unique identifier for revocation)
    pub jti: String,
    /// Subject (user ID)
    pub sub: String,
    /// Issued at timestamp
    pub iat: u64,
    /// Expiration timestamp
    pub exp: u64,
    /// Not before timestamp (optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub nbf: Option<u64>,
    /// Issuer
    pub iss: String,
    /// Audience
    pub aud: String,
    /// User roles
    pub roles: Vec<String>,
    /// Permissions
    pub permissions: Vec<String>,
    /// Token type (access/refresh)
    pub token_type: String,
    /// Session ID (optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,
}

/// JWT configuration matching the unified API
pub struct JwtConfig {
    pub secret: String,
    pub issuer: String,
    pub audience: String,
}

impl Default for JwtConfig {
    fn default() -> Self {
        // Precedence: env var > config file > dev fallback
        let resolved_secret = std::env::var("JWT_SECRET")
            .ok()
            .or_else(|| {
                crate::config::FxtConfig::load()
                    .ok()
                    .and_then(|c| c.jwt_secret)
            })
            .unwrap_or_else(|| {
                tracing::debug!("JWT_SECRET not set and no jwt_secret in config.toml - using development fallback");
                "test-secret-must-be-at-least-64-characters-long-for-security-validation-ok-1234567890".to_owned()
            });
        Self {
            secret: resolved_secret,
            issuer: std::env::var("JWT_ISSUER")
                .unwrap_or_else(|_| "foxhunt-api".to_owned()),
            audience: std::env::var("JWT_AUDIENCE")
                .unwrap_or_else(|_| "foxhunt-services".to_owned()),
        }
    }
}

/// Generate a valid JWT access token
///
/// Returns (token, jti) for token tracking.
///
/// # Arguments
/// * `user_id` - User identifier (e.g., "default")
/// * `roles` - User roles (e.g., vec!["`trader".to_string()`])
/// * `permissions` - User permissions (e.g., vec!["`api.access".to_string()`])
/// * `ttl_seconds` - Time to live in seconds (e.g., 900 for 15 minutes)
pub fn generate_access_token(
    user_id: &str,
    roles: Vec<String>,
    permissions: Vec<String>,
    ttl_seconds: u64,
) -> Result<(String, String)> {
    let config = JwtConfig::default();
    let jti = Uuid::new_v4().to_string();

    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();

    let claims = JwtClaims {
        jti: jti.clone(),
        sub: user_id.to_owned(),
        iat: now,
        exp: now + ttl_seconds,
        nbf: Some(now),
        iss: config.issuer,
        aud: config.audience,
        roles,
        permissions,
        token_type: "access".to_owned(),
        session_id: Some(Uuid::new_v4().to_string()),
    };

    let token = encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret(config.secret.as_bytes()),
    )?;

    Ok((token, jti))
}

/// Generate a valid JWT refresh token
///
/// Returns (token, jti) for token tracking.
///
/// # Arguments
/// * `user_id` - User identifier
/// * `ttl_seconds` - Time to live in seconds (e.g., 7200 for 2 hours)
pub fn generate_refresh_token(user_id: &str, ttl_seconds: u64) -> Result<(String, String)> {
    let config = JwtConfig::default();
    let jti = Uuid::new_v4().to_string();

    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();

    let claims = JwtClaims {
        jti: jti.clone(),
        sub: user_id.to_owned(),
        iat: now,
        exp: now + ttl_seconds,
        nbf: Some(now),
        iss: config.issuer,
        aud: config.audience,
        roles: vec!["trader".to_owned()],
        permissions: vec!["api.access".to_owned()],
        token_type: "refresh".to_owned(),
        session_id: Some(Uuid::new_v4().to_string()),
    };

    let token = encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret(config.secret.as_bytes()),
    )?;

    Ok((token, jti))
}
