# -*- coding: utf-8 -*-
#
# test_accountmanager.py
#
# Copyright 2016, 2018 Timo Kankare <timo.kankare@iki.fi>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#

"""Test cases for account manager."""

from configparser import RawConfigParser
from io import StringIO
import pytest

from Mailnag.backends import get_mailbox_parameter_specs
from Mailnag.common.accounts import AccountManager
from Mailnag.common.credentialstore import CredentialStore


class FakeCredentialStore(CredentialStore):
	"""Helper class to be used in tests."""

	def __init__(self):
		self.secrets = {}

	def get(self, key):
		return self.secrets[key]

	def set(self, key, secret):
		self.secrets[key] = secret

	def remove(self, key):
		if key in self.secrets:
			del self.secrets[key]


sample_config_file = """
[account1]
enabled = 1
name = IMAP mailbox config
user = you
password = drowssap
server = imap.example.org
port =
ssl = 1
imap = 1
idle = 1
folder = []

[account2]
enabled = 1
name = POP3 mailbox config
user = me
password = poppoppop
server = pop.example.org
port =
ssl = 1
imap = 0
idle = 0
folder = []

[account3]
enabled = 1
name = Empty account config for testing default values

[account4]
enabled = 1
name = Imap config with empty folder option
folder =

[account5]
enabled = 1
name = Imap config with old style folder option
folder = folderA, folderB, folderC

[account6]
enabled = 1
name = Imap config with json folder option
folder = ["folderA", "folderB", "folderC"]
"""

@pytest.fixture
def config():
	cp = RawConfigParser()
	cp.readfp(StringIO(sample_config_file), filename='sample_config_file')
	return cp


def test_imap_config_options(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account1', option_spec)
	expected_options = {
		'user': 'you',
		'password': 'drowssap',
		'server': 'imap.example.org',
		'port': '',
		'ssl': True,
		'imap': True,
		'idle': True,
		'folders': [],
	}
	assert expected_options == options


def test_imap_config_defaults(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account3', option_spec)
	expected_options = {
		'user': '',
		'password': '',
		'server': '',
		'port': '',
		'ssl': True,
		'imap': True,
		'idle': True,
		'folders': [],
	}
	assert expected_options == options


def test_imap_empty_folder_option(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account4', option_spec)
	assert options['folders'] == []


def test_imap_old_folder_option(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account5', option_spec)
	assert options['folders'] == ['folderA', 'folderB', 'folderC']


def test_imap_new_folder_option(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account6', option_spec)
	assert options['folders'] == ['folderA', 'folderB', 'folderC']


def test_pop3_config_options(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('pop3')
	options = am._get_cfg_options(config, 'account2', option_spec)
	expected_options = {
		'user': 'me',
		'password': 'poppoppop',
		'server': 'pop.example.org',
		'port': '',
		'ssl': True,
		'imap': False,
		'idle': False,
	}
	assert expected_options == options


def test_pop3_config_defaults(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('pop3')
	options = am._get_cfg_options(config, 'account3', option_spec)
	expected_options = {
		'user': '',
		'password': '',
		'server': '',
		'port': '',
		'ssl': True,
		'imap': False,
		'idle': False,
	}
	assert expected_options == options


def test_imap_config_values_should_be_stored():
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = {
		'user': 'you',
		'password': '',
		'server': 'imap.example.org',
		'port': '',
		'ssl': True,
		'imap': True,
		'idle': True,
		'folders': ['a', 'b'],
	}
	config = RawConfigParser()
	config.add_section('account1')
	am._set_cfg_options(config, 'account1', options, option_spec)
	expected_config_items = [
		('user', 'you'),
		('password', ''),
		('server', 'imap.example.org'),
		('port', ''),
		('ssl', '1'),
		('imap', '1'),
		('idle', '1'),
		('folder', '["a", "b"]'),
	]
	assert set(expected_config_items) == set(config.items('account1'))


# Load from config

def get_account(accounts, name):
	"""Finds and returns account which has given name."""
	return next(account for account in accounts if account.name == name)


def test_load_from_config(config):
	am = AccountManager()
	am.load_from_cfg(config, enabled_only=False)
	accounts = am.to_list()
	assert len(accounts) == 6
	imap_account = get_account(am.to_list(), 'IMAP mailbox config')
	pop3_account = get_account(am.to_list(), 'POP3 mailbox config')
	assert imap_account.get_config()['password'] == 'drowssap'
	assert pop3_account.get_config()['password'] == 'poppoppop'


def test_load_from_config_with_credential_store(config):
	cs = FakeCredentialStore()
	cs.set('Mailnag password for imap://you@imap.example.org', 'verry seecret')
	cs.set('Mailnag password for pop://me@pop.example.org', 'seecret too')
	am = AccountManager(cs)
	am.load_from_cfg(config, enabled_only=False)
	accounts = am.to_list()
	assert len(accounts) == 6
	imap_account = get_account(am.to_list(), 'IMAP mailbox config')
	pop3_account = get_account(am.to_list(), 'POP3 mailbox config')
	assert imap_account.get_config()['password'] == 'verry seecret'
	assert pop3_account.get_config()['password'] == 'seecret too'


# Save to config

def test_save_zero_accounts_to_config(config):
	am = AccountManager()
	am.save_to_cfg(config)
	assert len(config.sections()) == 0


def test_save_all_accounts_to_config(config):
	am = AccountManager()
	am.load_from_cfg(config, enabled_only=False)
	am.save_to_cfg(config)
	assert len(config.sections()) == 6


def test_save_zero_accounts_to_config_with_credential_store(config):
	cs = FakeCredentialStore()
	am = AccountManager(cs)
	am.save_to_cfg(config)
	assert len(config.sections()) == 0
	assert cs.secrets == {}


def test_save_all_accounts_to_config_with_credential_store(config):
	cs = FakeCredentialStore()
	cs.set('Mailnag password for imap://you@imap.example.org', 'verry seecret')
	cs.set('Mailnag password for pop://me@pop.example.org', 'seecret too')
	am = AccountManager(cs)
	am.load_from_cfg(config, enabled_only=False)
	am.save_to_cfg(config)
	assert len(config.sections()) == 6
	assert cs.secrets == {
	    'Mailnag password for imap://@': '',
	    'Mailnag password for imap://you@imap.example.org': 'verry seecret',
	    'Mailnag password for pop://me@pop.example.org': 'seecret too'
	}


def test_save_removed_accounts_to_config_with_credential_store(config):
	cs = FakeCredentialStore()
	cs.set('Mailnag password for imap://you@imap.example.org', 'verry seecret')
	cs.set('Mailnag password for pop://me@pop.example.org', 'seecret too')
	am = AccountManager(cs)
	am.load_from_cfg(config, enabled_only=False)
	am.clear()
	am.save_to_cfg(config)
	assert len(config.sections()) == 0
	assert cs.secrets == {}

