extern crate actix_web;
extern crate chrono;
extern crate crypto;
extern crate jsonwebtoken as jwt;
#[macro_use]
extern crate log;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate uuid;

pub mod couchdb;
pub mod security;
pub mod web;

use std::collections::HashMap;

use chrono::{DateTime, Utc};
use serde_json::Value;

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct UserProfile {
    email: String,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct User {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<String>,
    #[serde(rename = "_rev", skip_serializing_if = "Option::is_none")]
    revision: Option<String>,
    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
    type_: Option<String>,

    pub username: String,
    #[serde(rename = "fullName")]
    full_name: String,
    pub password: String,
    pub profile: UserProfile,

    #[serde(skip_deserializing)]
    links: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Page {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<String>,
    #[serde(rename = "_rev", skip_serializing_if = "Option::is_none")]
    revision: Option<String>,
    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
    type_: Option<String>,

    pub slug: String,
    title: String,
    content: String,
    #[serde(rename = "created", skip_deserializing)]
    created_at: Option<DateTime<Utc>>,

    #[serde(skip_deserializing)]
    links: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MenuItems {
    title: String,
    url: String,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Site {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<String>,
    #[serde(rename = "_rev", skip_serializing_if = "Option::is_none")]
    revision: Option<String>,
    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
    type_: Option<String>,

    name: String,
    #[serde(rename = "landingPage")]
    landing_page: String,
    header: String,
    footer: String,
    menu: Vec<MenuItems>,

    config: HashMap<String, Value>,

    #[serde(skip_deserializing)]
    links: HashMap<String, Value>,
}

pub trait RookeriesDomain {
    fn id(&self) -> Option<String>;
    fn remove_couchdb_details(&self) -> Self;
    fn add_self_links(&self, self_link: String) -> Self;
}

// TODO: Figure out how to set these as up default implementations.
impl RookeriesDomain for Page {
    fn id(&self) -> Option<String> {
        self.id.clone()
    }

    fn remove_couchdb_details(&self) -> Page {
        Page {
            id: None,
            revision: None,
            type_: None,
            ..self.clone()
        }
    }

    fn add_self_links(&self, self_link: String) -> Page {
        let mut links = HashMap::new();
        links.insert(String::from("self"), Value::String(self_link));

        Page {
            links,
            ..self.clone()
        }
    }
}

impl RookeriesDomain for Site {
    fn id(&self) -> Option<String> {
        self.id.clone()
    }

    fn remove_couchdb_details(&self) -> Site {
        Site {
            id: None,
            revision: None,
            type_: None,
            ..self.clone()
        }
    }

    fn add_self_links(&self, self_link: String) -> Site {
        let mut links = HashMap::new();
        links.insert(String::from("self"), Value::String(self_link));

        Site {
            links,
            ..self.clone()
        }
    }
}
