use crate::feed_api::{FeedApiErrorKind, FeedApiResult};
use crate::password_encryption::PasswordEncryption;
use failure::ResultExt;
use log::{error, info};
use serde_derive::{Deserialize, Serialize};
use std::fs::{self, File};
use std::io::Read;
use std::path::PathBuf;

static CONFIG_NAME: &'static str = "fever.json";

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AccountConfig {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    user_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    password: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    http_user_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    http_password: Option<String>,
    #[serde(skip_serializing)]
    #[serde(skip_deserializing)]
    path: PathBuf,
}

impl AccountConfig {
    pub fn load(path: &PathBuf) -> FeedApiResult<Self> {
        let path = path.join(CONFIG_NAME);
        if path.as_path().exists() {
            let mut contents = String::new();
            info!("Attempting to open config file: {:?}", path);
            let mut file = File::open(&path)
                .map_err(move |err| {
                    error!("Failed to load config file");
                    err
                })
                .context(FeedApiErrorKind::IO)?;
            file.read_to_string(&mut contents)
                .map_err(move |err| {
                    error!("Reading content of file failed");
                    err
                })
                .context(FeedApiErrorKind::IO)?;
            let mut config: AccountConfig = serde_json::from_str(&contents).context(FeedApiErrorKind::Config)?;

            if let Some(password) = config.password {
                let password = PasswordEncryption::decrypt(&password).context(FeedApiErrorKind::Encryption)?;
                config.password = Some(password);
            }
            if let Some(http_pass) = config.http_password {
                let http_pass = PasswordEncryption::decrypt(&http_pass).context(FeedApiErrorKind::Encryption)?;
                config.http_password = Some(http_pass);
            }

            config.path = path;

            return Ok(config);
        }

        info!("Config file does not exist. Returning empty config: {:?}", path);
        Ok(AccountConfig {
            user_name: None,
            password: None,
            url: None,
            path: path.clone(),
            http_user_name: None,
            http_password: None,
        })
    }

    pub fn write(&self) -> FeedApiResult<()> {
        let mut config = self.clone();
        if let Some(password) = config.get_password() {
            let password = PasswordEncryption::encrypt(&password).context(FeedApiErrorKind::Encryption)?;
            config.set_password(&password);
        }
        if let Some(http_pass) = config.get_http_password() {
            let http_pass = PasswordEncryption::encrypt(&http_pass).context(FeedApiErrorKind::Encryption)?;
            config.set_http_password(Some(http_pass).as_deref());
        }
        let data = serde_json::to_string_pretty(&config).context(FeedApiErrorKind::Config)?;
        fs::write(&self.path, data)
            .map_err(|err| {
                error!("Failed to write config to: {:?}", self.path);
                err
            })
            .context(FeedApiErrorKind::IO)?;
        Ok(())
    }

    pub fn delete(&self) -> FeedApiResult<()> {
        fs::remove_file(&self.path).context(FeedApiErrorKind::IO)?;
        Ok(())
    }

    pub fn get_user_name(&self) -> Option<String> {
        self.user_name.clone()
    }

    pub fn set_user_name(&mut self, user_name: &str) {
        self.user_name = Some(user_name.to_owned());
    }

    pub fn get_password(&self) -> Option<String> {
        self.password.clone()
    }

    pub fn set_password(&mut self, password: &str) {
        self.password = Some(password.to_owned());
    }

    pub fn get_http_user_name(&self) -> Option<String> {
        self.http_user_name.clone()
    }

    pub fn set_http_user_name(&mut self, user_name: Option<&str>) {
        self.http_user_name = user_name.map(|s| s.into());
    }

    pub fn get_http_password(&self) -> Option<String> {
        self.http_password.clone()
    }

    pub fn set_http_password(&mut self, password: Option<&str>) {
        self.http_password = password.map(|s| s.into());
    }

    pub fn get_url(&self) -> Option<String> {
        self.url.clone()
    }

    pub fn set_url(&mut self, url: &str) {
        self.url = Some(url.to_owned());
    }
}
