use crate::types::payments::card::bin_digits::BinDigits;
use crate::types::payments::card::brand::CardBrand;
use crate::types::payments::card::card_holder::CardholderName;
use crate::types::payments::card::expiration_date::ExpirationDate;
use crate::types::payments::card::last_digits::LastDigits;
use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::str::FromStr;

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Card {
    pub bin_digits: BinDigits,
    pub last_digits: LastDigits,
    pub expiration_date: ExpirationDate,
    pub name: CardholderName,
    pub brand: CardBrand,
}

impl Card {
    pub fn new(
        bin_digits: BinDigits,
        last_digits: LastDigits,
        expiration_date: ExpirationDate,
        name: CardholderName,
        brand: CardBrand,
    ) -> Self {
        Self {
            bin_digits,
            last_digits,
            expiration_date,
            name,
            brand,
        }
    }

    pub fn from_strings(
        bin: impl Into<String>,
        last: impl Into<String>,
        exp: impl Into<String>,
        name: impl Into<String>,
        brand: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        Ok(Self {
            bin_digits: BinDigits::try_new(bin)?,
            last_digits: LastDigits::try_new(last)?,
            expiration_date: ExpirationDate::from_str(&exp.into())?,
            name: CardholderName::try_new(name)?,
            brand: CardBrand::try_from(brand.into())?,
        })
    }

    pub fn masked_number(&self) -> String {
        format!("{}******{}", self.bin_digits, self.last_digits)
    }

    pub fn is_expired(&self) -> bool {
        self.expiration_date.is_expired()
    }
}
