use std::time::Duration;

#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
pub struct SizeConstraint {
    min: usize,
    max: usize,
}

impl SizeConstraint {
    pub fn new(min: usize, max: usize) -> Self {
        Self { min, max }
    }

    pub fn validate(&self, val: &str) -> bool {
        let len = val.trim().len();
        len >= self.min && len <= self.max
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NumberValueConstraint {
    min: i64,
    max: i64,
}

impl NumberValueConstraint {
    pub fn new(min: i64, max: i64) -> Self {
        Self { min, max }
    }

    pub fn validate(&self, val: impl Into<i64>) -> bool {
        let val = val.into();
        val >= self.min && val <= self.max
    }
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct StaticConstraints {
    pub date_year: NumberValueConstraint,
    pub date_month: NumberValueConstraint,
    pub date_day: NumberValueConstraint,

    pub photo_allowed_types: &'static [&'static str],
    pub photo_max_size: usize,

    pub mail_len: SizeConstraint,
    pub user_name_len: SizeConstraint,
    pub password_len: SizeConstraint,
    pub family_name_len: SizeConstraint,
    pub invitation_code_len: SizeConstraint,

    pub member_first_name: SizeConstraint,
    pub member_last_name: SizeConstraint,
    pub member_birth_last_name: SizeConstraint,
    pub member_email: SizeConstraint,
    pub member_phone: SizeConstraint,
    pub member_address: SizeConstraint,
    pub member_city: SizeConstraint,
    pub member_postal_code: SizeConstraint,
    pub member_country: SizeConstraint,
    pub member_sex: SizeConstraint,
    pub member_note: SizeConstraint,
}

impl Default for StaticConstraints {
    fn default() -> Self {
        Self {
            date_year: NumberValueConstraint::new(1, 2050),
            date_month: NumberValueConstraint::new(1, 12),
            date_day: NumberValueConstraint::new(1, 31),

            photo_allowed_types: &ALLOWED_PHOTOS_MIMETYPES,
            photo_max_size: PHOTOS_MAX_SIZE,

            mail_len: SizeConstraint::new(5, 255),
            user_name_len: SizeConstraint::new(3, 30),
            password_len: SizeConstraint::new(8, 255),
            family_name_len: SizeConstraint::new(3, 30),
            invitation_code_len: SizeConstraint::new(
                FAMILY_INVITATION_CODE_LEN,
                FAMILY_INVITATION_CODE_LEN,
            ),
            member_first_name: SizeConstraint::new(0, 30),
            member_last_name: SizeConstraint::new(0, 30),
            member_birth_last_name: SizeConstraint::new(0, 30),
            member_email: SizeConstraint::new(0, 255),
            member_phone: SizeConstraint::new(0, 30),
            member_address: SizeConstraint::new(0, 155),
            member_city: SizeConstraint::new(0, 150),
            member_postal_code: SizeConstraint::new(0, 12),
            member_country: SizeConstraint::new(0, 2),
            member_sex: SizeConstraint::new(0, 1),
            member_note: SizeConstraint::new(0, 35000),
        }
    }
}

/// Password reset token duration
pub const PASSWORD_RESET_TOKEN_DURATION: Duration = Duration::from_secs(3600 * 24);

/// Account deletion token duration
pub const ACCOUNT_DELETE_TOKEN_DURATION: Duration = Duration::from_secs(3600 * 12);

/// OpenID state duration
pub const OPEN_ID_STATE_DURATION: Duration = Duration::from_secs(3600);

/// Auth token duration
pub const AUTH_TOKEN_DURATION: Duration = Duration::from_secs(3600 * 24);

/// Minimum interval before heartbeat update
pub const AUTH_TOKEN_HB_MIN_INTERVAL: Duration = Duration::from_secs(60);

/// Auth token max inactivity period
pub const AUTH_TOKEN_MAX_INACTIVITY: Duration = Duration::from_secs(3600);

/// Length of family invitation code
pub const FAMILY_INVITATION_CODE_LEN: usize = 7;

/// Allowed photos mimetypes
pub const ALLOWED_PHOTOS_MIMETYPES: [&str; 6] = [
    "image/jpeg",
    "image/png",
    "image/gif",
    "image/bmp",
    "image/webp",
    "image/vnd.microsoft.icon",
];

/// Uploaded photos max size
pub const PHOTOS_MAX_SIZE: usize = 10 * 1000 * 1000;

/// Thumbnail width
pub const THUMB_WIDTH: u32 = 350;

/// Thumbnail height
pub const THUMB_HEIGHT: u32 = 350;