//Anything related to POST requests for Teams and it's variations goes here.
use super::{Team, TeamApp, TeamInvitation, TeamMember};

use crate::framework::endpoint::{HerokuEndpoint, Method};

/// Team Create
///
/// Create a new team.
///
/// [See Heroku documentation for more information about this endpoint](https://devcenter.heroku.com/articles/platform-api-reference#team-create)
pub struct TeamCreate<'a> {
    pub params: TeamCreateParams<'a>,
}

impl<'a> TeamCreate<'a> {
    // `new` method has the required parameters & optional ones
    pub fn new(
        name: &'a str,
        address_1: Option<&'a str>,
        address_2: Option<&'a str>,
        card_number: Option<&'a str>,
        city: Option<&'a str>,
        country: Option<&'a str>,
        cvv: Option<&'a str>,
        expiration_month: Option<&'a str>,
        expiration_year: Option<&'a str>,
        first_name: Option<&'a str>,
        last_name: Option<&'a str>,
        other: Option<&'a str>,
        postal_code: Option<&'a str>,
        state: Option<&'a str>,
        nonce: Option<&'a str>,
        device_data: Option<&'a str>,
    ) -> TeamCreate<'a> {
        TeamCreate {
            params: TeamCreateParams {
                name,
                params_optional: Some(TeamCreateOptionalParams {
                    address_1,
                    address_2,
                    card_number,
                    city,
                    country,
                    cvv,
                    expiration_month,
                    expiration_year,
                    first_name,
                    last_name,
                    other,
                    postal_code,
                    state,
                    nonce,
                    device_data,
                }),
            },
        }
    }

    // `create` method has only the required parameters
    pub fn create(name: &'a str) -> TeamCreate {
        TeamCreate {
            params: TeamCreateParams {
                name: name,
                params_optional: None,
            },
        }
    }
}

/// Create a new team with parameters
///
/// Only the name is required
///
/// [See Heroku documentation for more information about these paramters](https://devcenter.heroku.com/articles/platform-api-reference#team-create-required-parameters)
#[serde_with::skip_serializing_none]
#[derive(Serialize, Clone, Debug)]
pub struct TeamCreateParams<'a> {
    /// unique name of team
    pub name: &'a str,
    /// parameters to pass to Heroku
    #[serde(flatten)]
    pub params_optional: Option<TeamCreateOptionalParams<'a>>,
}

/// Create a new team with optional parameters
///
/// [See Heroku documentation for more information about these paramters](https://devcenter.heroku.com/articles/platform-api-reference#team-create-optional-parameters)
#[serde_with::skip_serializing_none]
#[derive(Serialize, Clone, Debug)]
pub struct TeamCreateOptionalParams<'a> {
    /// street address line 1
    pub address_1: Option<&'a str>,
    /// street address line 2
    pub address_2: Option<&'a str>,
    /// encrypted card number of payment method
    pub card_number: Option<&'a str>,
    /// city
    pub city: Option<&'a str>,
    /// country
    pub country: Option<&'a str>,
    /// card verification value
    pub cvv: Option<&'a str>,
    /// expiration month
    pub expiration_month: Option<&'a str>,
    /// expiration year
    pub expiration_year: Option<&'a str>,
    /// the first name for payment method
    pub first_name: Option<&'a str>,
    /// the last name for payment method
    pub last_name: Option<&'a str>,
    /// metadata
    pub other: Option<&'a str>,
    /// postal code
    pub postal_code: Option<&'a str>,
    /// state
    pub state: Option<&'a str>,
    /// Nonce generated by Braintree hosted fields form
    pub nonce: Option<&'a str>,
    /// Device data string generated by the client
    pub device_data: Option<&'a str>,
}

impl<'a> HerokuEndpoint<Team, (), TeamCreateParams<'a>> for TeamCreate<'a> {
    fn method(&self) -> Method {
        Method::Post
    }
    fn path(&self) -> String {
        format!("teams")
    }
    fn body(&self) -> Option<TeamCreateParams<'a>> {
        Some(self.params.clone())
    }
}

/// Team Create in Enterprise Account
///
/// Create a team in an enterprise account.
///
/// [See Heroku documentation for more information about this endpoint](https://devcenter.heroku.com/articles/platform-api-reference#team-create-in-enterprise-account)
pub struct TeamCreateByEA<'a> {
    /// unique account identifier
    pub account_id: &'a str,
    /// parameters to pass to Heroku
    pub params: TeamCreateByEAParams<'a>,
}

impl<'a> TeamCreateByEA<'a> {
    pub fn new(account_id: &'a str, name: &'a str) -> TeamCreateByEA<'a> {
        TeamCreateByEA {
            account_id: account_id,
            params: TeamCreateByEAParams { name },
        }
    }
}

/// Create a new team in an enterprise account with required parametesrs
///
/// [See Heroku documentation for more information about these paramters](https://devcenter.heroku.com/articles/platform-api-reference#team-create-in-enterprise-account-required-parameters)
#[derive(Serialize, Clone, Debug)]
pub struct TeamCreateByEAParams<'a> {
    /// unique name of team
    pub name: &'a str,
}

impl<'a> HerokuEndpoint<Team, (), TeamCreateByEAParams<'a>> for TeamCreateByEA<'a> {
    fn method(&self) -> Method {
        Method::Post
    }
    fn path(&self) -> String {
        format!("enterprise-accounts/{}/teams", self.account_id)
    }
    fn body(&self) -> Option<TeamCreateByEAParams<'a>> {
        Some(self.params.clone())
    }
}

/// Team App Create
///
/// Create a new app in the specified team, in the default team if unspecified, or in personal account, if default team is not set.
///
/// [See Heroku documentation for more information about this endpoint](https://devcenter.heroku.com/articles/platform-api-reference#team-app-create)
pub struct TeamAppCreate<'a> {
    pub params: Option<TeamAppCreateParams<'a>>,
}

impl<'a> TeamAppCreate<'a> {
    pub fn new(
        locked: Option<bool>,
        name: Option<&'a str>,
        team: Option<&'a str>,
        personal: Option<bool>,
        region: Option<&'a str>,
        space: Option<&'a str>,
        stack: Option<&'a str>,
        internal_routing: Option<bool>,
    ) -> TeamAppCreate<'a> {
        TeamAppCreate {
            params: Some(TeamAppCreateParams {
                locked,
                name,
                team,
                personal,
                region,
                space,
                stack,
                internal_routing,
            }),
        }
    }

    /// Pass no parameters to the Heroku API
    pub fn create() -> TeamAppCreate<'a> {
        TeamAppCreate { params: None }
    }
}

/// Create a new team app with parameters
///
/// All parameters are optional
///
/// [See Heroku documentation for more information about these paramters](https://devcenter.heroku.com/articles/platform-api-reference#team-app-create-optional-parameters)
#[serde_with::skip_serializing_none]
#[derive(Serialize, Clone, Debug)]
pub struct TeamAppCreateParams<'a> {
    /// are other team members forbidden from joining this app.
    pub locked: Option<bool>,
    /// name of app
    /// pattern: ^[a-z][a-z0-9-]{1,28}[a-z0-9]$
    pub name: Option<&'a str>,
    /// unique name of team
    pub team: Option<&'a str>,
    /// force creation of the app in the user account even if a default team is set.
    pub personal: Option<bool>,
    /// name of region
    pub region: Option<&'a str>,
    /// unique name of space
    ///  pattern: `^[a-z0-9](?:[a-z0-9]
    pub space: Option<&'a str>,
    /// unique name
    pub stack: Option<&'a str>,
    /// describes whether a Private Spaces app is externally routable or not
    pub internal_routing: Option<bool>,
}

impl<'a> HerokuEndpoint<TeamApp, (), TeamAppCreateParams<'a>> for TeamAppCreate<'a> {
    fn method(&self) -> Method {
        Method::Post
    }
    fn path(&self) -> String {
        format!("teams/apps")
    }
    fn body(&self) -> Option<TeamAppCreateParams<'a>> {
        self.params.clone()
    }
}

/// Team Invitation Accept
///
/// Accept Team Invitation
///
/// [See Heroku documentation for more information about this endpoint](https://devcenter.heroku.com/articles/platform-api-reference#team-invitation-accept)
pub struct TeamInvitationAccept<'a> {
    /// unique token identifier
    pub token_id: &'a str,
}

impl<'a> TeamInvitationAccept<'a> {
    pub fn new(token_id: &'a str) -> TeamInvitationAccept<'a> {
        TeamInvitationAccept { token_id }
    }
}

impl<'a> HerokuEndpoint<TeamInvitation> for TeamInvitationAccept<'a> {
    fn method(&self) -> Method {
        Method::Post
    }
    fn path(&self) -> String {
        format!("teams/invitations/{}/accept", self.token_id)
    }
}

/// Team Member Create
///
/// Create a new team member.
///
/// [See Heroku documentation for more information about this endpoint](https://devcenter.heroku.com/articles/platform-api-reference#team-member-create)
pub struct TeamMemberCreate<'a> {
    /// unique team identifier
    pub team_id: &'a str,
    /// parameters to pass to Heroku
    pub params: TeamMemberCreateParams<'a>,
}

impl<'a> TeamMemberCreate<'a> {
    /// required and optional parameters
    pub fn new(
        team_id: &'a str,
        email: &'a str,
        role: &'a str,
        federated: Option<bool>,
    ) -> TeamMemberCreate<'a> {
        TeamMemberCreate {
            team_id,
            params: TeamMemberCreateParams {
                email,
                role,
                federated,
            },
        }
    }
    /// Only required parameters passed
    pub fn create(team_id: &'a str, email: &'a str, role: &'a str) -> TeamMemberCreate<'a> {
        TeamMemberCreate {
            team_id,
            params: TeamMemberCreateParams {
                email: email,
                role: role,
                federated: None,
            },
        }
    }
}

/// Create team member with parameters
///
/// [See Heroku documentation for more information about these paramters](https://devcenter.heroku.com/articles/platform-api-reference#team-member-create-required-parameters)
#[serde_with::skip_serializing_none]
#[derive(Serialize, Clone, Debug)]
pub struct TeamMemberCreateParams<'a> {
    /// unique email address
    pub email: &'a str,
    /// Even though marked with `Option`, this parameter is NOT optional.
    /// role in the team
    /// one of:"admin" or "collaborator" or "member" or "owner" or null
    pub role: &'a str,
    /// whether the user is federated and belongs to an Identity Provider
    pub federated: Option<bool>,
}

impl<'a> HerokuEndpoint<TeamMember, (), TeamMemberCreateParams<'a>> for TeamMemberCreate<'a> {
    fn method(&self) -> Method {
        Method::Post
    }
    fn path(&self) -> String {
        format!("teams/{}/members", self.team_id)
    }
    fn body(&self) -> Option<TeamMemberCreateParams<'a>> {
        Some(self.params.clone())
    }
}
