use serde::{Deserialize, Serialize};
use crate::model::{id::Id, oauth::AppScope};
use tritools::time::unix_epoch_timestamp;

/// An app is required to request grants on user accounts.
///
/// Users must approve grants through a web portal.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ThirdPartyApp {
    pub id: Id,
    pub created: u128,
    /// The ID of the owner of the app.
    pub owner: crate::model::id::Id,
    /// The name of the app.
    pub title: String,
    /// The URL of the app's homepage.
    pub homepage: String,
    /// The redirect URL for the app.
    ///
    /// Upon accepting a grant request, the user will be redirected to this URL
    /// with a query parameter named `token`, which should be saved by the app
    /// for future authentication.
    ///
    /// The developer dashboard lists the URL you should send users to in order to
    /// create a grant on their account in the information section under the label
    /// "Grant URL".
    ///
    /// Any search parameters sent with your grant URL (such as an internal user ID)
    /// will also be sent back when the user is redirected to your redirect URL.
    ///
    /// You can use this behaviour to keep track of what user you should save the grant
    /// token under.
    ///
    /// 1. Redirect user to grant URL with their ID: `{grant_url}?my_app_user_id={id}`
    /// 2. In your redirect endpoint, read that ID and the added `token` parameter to
    /// store the `token` under the given `my_app_user_id`
    ///
    /// The redirect URL will also have a `verifier` search parameter appended.
    /// This verifier is required to refresh the grant's token (which is what is
    /// used in the `Atto-Grant` cookie).
    ///
    /// Tokens only last a week after they were generated (with the verifier),
    /// but you can refresh them by sending a request to:
    /// `{tetratto}/api/v2/users/{user_id}/grants/{app_id}/refresh`.
    ///
    /// Tetratto will generate the verifier and challenge for you. The challenge
    /// is an SHA-256 hashed + base64 url encoded version of the verifier. This means
    /// if the verifier doesn't match, it won't pass the challenge.
    ///
    /// Requests to API endpoints using your grant token should be sent with a
    /// cookie (in the `Cookie` or `X-Cookie` header) named `Atto-Grant`. This cookie should
    /// contain the token you received from either the initial connection,
    /// or a token refresh.
    pub redirect: String,
    /// If the app is banned. A banned app cannot use any of its grants.
    pub banned: bool,
    /// The number of accepted grants the app maintains.
    pub grants: usize,
    /// The scopes used for every grant the app maintains.
    ///
    /// These scopes are only cloned into **new** grants created for the app.
    /// An app *cannot* change scopes and have them affect users who already have the
    /// app connected. Users must delete the app's grant and authenticate it again
    /// to update their scopes.
    ///
    /// Your app should handle informing users when scopes change.
    pub scopes: Vec<AppScope>,
    /// The app's secret API key (for app_data access).
    pub api_key: String,
}

impl ThirdPartyApp {
    /// Create a new [`ThirdPartyApp`].
    pub fn new(
        title: String,
        owner: crate::model::id::Id,
        homepage: String,
        redirect: String,
    ) -> Self {
        Self {
            id: Id::new(),
            created: unix_epoch_timestamp(),
            owner,
            title,
            homepage,
            redirect,
            banned: false,
            grants: 0,
            scopes: Vec::new(),
            api_key: String::new(),
        }
    }
}
