//! Credentials type definitions for CLI authentication
//!
//! Types for storing and managing user credentials and device code flow.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Stored credentials for authenticated user
///
/// Saved to ~/.mecha10/credentials.json after successful login.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Credentials {
    /// User's API key for authenticating with control plane
    pub api_key: String,

    /// User ID from the auth system
    pub user_id: String,

    /// User's email address
    pub email: String,

    /// User's display name
    #[serde(default)]
    pub name: Option<String>,

    /// When the credentials were obtained
    pub authenticated_at: DateTime<Utc>,

    /// The auth server URL used for authentication
    pub auth_url: String,
}

impl Credentials {
    /// Check if credentials appear valid (non-empty api_key)
    pub fn is_valid(&self) -> bool {
        !self.api_key.is_empty() && self.api_key.starts_with("mecha_")
    }

    /// Get a masked version of the API key for display
    #[allow(dead_code)]
    pub fn masked_api_key(&self) -> String {
        if self.api_key.len() > 12 {
            format!("{}...{}", &self.api_key[..12], &self.api_key[self.api_key.len() - 4..])
        } else {
            "***".to_string()
        }
    }
}

/// Response from device code request
///
/// Returned by POST /auth/device/code
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceCodeResponse {
    /// The device code for backend polling
    pub device_code: String,

    /// User-facing code to enter in browser
    pub user_code: String,

    /// URL for user to visit
    pub verification_uri: String,

    /// Seconds until the code expires
    pub expires_in: u32,

    /// Seconds to wait between poll attempts
    pub interval: u32,
}

/// Status of a device code authorization
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum DeviceCodeStatus {
    /// User hasn't completed authorization yet
    Pending,

    /// User authorized - includes credentials
    Authorized {
        api_key: String,
        user_id: String,
        email: String,
        name: Option<String>,
    },

    /// User denied the authorization
    Denied,

    /// Device code has expired
    Expired,
}

impl DeviceCodeStatus {
    /// Check if this is a terminal state (no more polling needed)
    #[allow(dead_code)]
    pub fn is_terminal(&self) -> bool {
        !matches!(self, DeviceCodeStatus::Pending)
    }

    /// Check if authorization was successful
    #[allow(dead_code)]
    pub fn is_authorized(&self) -> bool {
        matches!(self, DeviceCodeStatus::Authorized { .. })
    }
}

/// Error types for authentication operations
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "error", rename_all = "snake_case")]
pub enum AuthError {
    /// Network or connection error
    NetworkError { message: String },

    /// Server returned an error
    ServerError { message: String, status_code: Option<u16> },

    /// Device code expired before user completed auth
    ExpiredCode,

    /// User denied the authorization request
    AccessDenied,

    /// Credentials file corrupted or invalid
    InvalidCredentials { message: String },

    /// Rate limited by server
    RateLimited { retry_after: Option<u32> },
}

impl std::fmt::Display for AuthError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AuthError::NetworkError { message } => write!(f, "Network error: {}", message),
            AuthError::ServerError { message, status_code } => {
                if let Some(code) = status_code {
                    write!(f, "Server error ({}): {}", code, message)
                } else {
                    write!(f, "Server error: {}", message)
                }
            }
            AuthError::ExpiredCode => write!(f, "Device code expired"),
            AuthError::AccessDenied => write!(f, "Access denied by user"),
            AuthError::InvalidCredentials { message } => write!(f, "Invalid credentials: {}", message),
            AuthError::RateLimited { retry_after } => {
                if let Some(secs) = retry_after {
                    write!(f, "Rate limited, retry after {} seconds", secs)
                } else {
                    write!(f, "Rate limited")
                }
            }
        }
    }
}

impl std::error::Error for AuthError {}
