#!/usr/bin/env python
# coding:utf-8

import os
import configparser
import json
from pykeepass import PyKeePass
import pykeepass.exceptions

from cplLdaptool import LdapMod

from .plugin import ModList
from .samba4ad import Samba4AD


msg0002 = 'unable to retrieve ldap data for: \'{0}\''
msg0003 = 'fetching configuration for: \'{0}\''
msg0004 = 'unable to parse config file \'{0}\': {1}'
msg0005 = 'no password store defined'
msg0006 = 'please specify ldap searchbase in config'
msg0007 = 'unexpected missing config: {1}'
msg0008 = 'admin as well as archive password should be assigned in redknife config, missing: {0}'
msg0009 = 'unknown password database file typ: \'{0}\''
msg0010 = 'no password found for account: \'{0}\''
msg0011 = 'unable to retrieve Keepass DB password'
msg0012 = 'unable to open Keepass DB'
msg0013 = 'password file \'{0}\' not found'
msg0014 = 'provided .json file doesn\'t look like a bitwarden/vaultwarden password export'
msg0015 = 'encrypted vautwarden files not supported'
msg0016 = 'no \'{0}\' in .json file'
msg0017 = 'LDAP objectClass not implemented'


redknife_cfile = '/root/redknife.conf'


class ProvisionSambaAD(Samba4AD, metaclass=ModList):

	def __init__(self, postcmd):

		self.class_name = self.__class__.__name__ 

		# get config-, logger-, ldap- and exit
		self.config = postcmd.getConfig()
		self.logger = postcmd.getLogger()
		self.exit = postcmd.getExit()
		self.gosa_ldap = postcmd.getGosaLdap()
		self.ldapmod = LdapMod(self.gosa_ldap, self.logger)

		super().__init__(postcmd)

		self.searchbase = postcmd.gosa_ldap_cfg['searchbase']
		if not self.searchbase:
			self.exit(254, msg0006)

		# get provision password list provider
		self.redknife_conf = self.getRedknifeConfig()
		if not self.redknife_conf:
			self.pprovider = lambda user: None
			return
			# there is no redknife.conf, so we can not return passwords

		# figure out password store from redknife.conf
		try:
			#pwd_store = self.redknife_conf.get(configparser.UNNAMED_SECTION, 'pwd_store')
			pwd_store = self.redknife_conf['none']['pwd_store']
			
		except KeyError:

			# no further passwords store defined with pwd_store
			# so we use the few passwords provided in redknife.conf
			# for the default accounts, mosty the admin password
			self.logger.debug(msg0005)

			try:
				admin_pw = self.redknife_conf['none']['admin_pw']
				archiv_pw = self.redknife_conf['none']['archiv_pw']

			except KeyError as e:
				#self.exit(254, msg0007.format(e))
				self.logger.error(msg0008.format(e))
				self.exit(254, '')

			self.static_pw = {
				'admin' : admin_pw,
				'administrator' : admin_pw,
				'adressbuch' : admin_pw,
				'archivmaster' : archiv_pw,
				'cloudshare' : admin_pw,
				'scanner' : admin_pw,
				}

			self.pprovider = self.pwdStatic
			return

		# take the provided path appart
		pwd_store_path = pwd_store.split(os.path.sep)
		file_name = pwd_store_path[-1].split('.')
		self.user_filter = self.redknife_conf['none']['user_filter']

		# keepass files
		if file_name[-1] == 'kdbx':

			try:
				pwd_store_pw = self.redknife_conf['none']['pwd_store_pw']

			except IndexError:
				self.logger.error(msg0011)
				self.exit(254, '')

			try:
				self.kp = PyKeePass(pwd_store, password=pwd_store_pw)

			except pykeepass.exceptions.CredentialsError:
				self.logger.error(msg0012)
				self.exit(254, '')

			self.pprovider = self.pwdKeepass

		# .json files we treat as vaultwarden exports
		elif file_name[-1] == 'json':

			try:
				with open(pwd_store, 'r') as stream:
					self.vw = json.loads(stream.read())

			except FileNotFoundError:
				self.logger.error(msg0013.format(pwd_store))
				self.exit(254, '')

			if 'items' not in self.vw:
				self.logger.error(msg0014)
				self.exit(254, '')

			if self.vw.get("encrypted"):
				self.logger.error(msg0015)
				self.exit(254, '')
				# maybe implement from
				# https://github.com/GurpreetKang/BitwardenDecrypt

			# look for user filter collection or folder
			self.vw_filter_id = {}
			for oname in ('folders', 'collections', ):

				obj = self.vw.get(oname)
				if not obj:
					self.logger.debug(msg0016.format(oname))
					continue

				for item in obj:
					if item.get('name') == self.user_filter:
						self.vw_filter_id[oname] = item.get('id')
						break

			self.pprovider = self.pwdVaultwarden
		else:
			self.logger.error(msg0009.format(file_name[1]))
			self.exit(254, '')


	def pwdStatic(self, username):

		try:
			return self.static_pw[username]
		except KeyError:
			return None


	def pwdKeepass(self, username):

		user_filter = self.kp.find_groups(name=self.user_filter, first=True)
		entry = self.kp.find_entries(username=username, group=user_filter, first=True)

		try:
			return entry.password

		except AttributeError:
			return None


	def pwdVaultwarden(self, username):

		for item in self.vw['items']:

			try:
				if item['login']['username'] == username and \
					('folderId' in item and \
					self.vw_filter_id['folders'] in item['folderId']) or \
					('collectionIds' in item and \
					self.vw_filter_id['collections'] in item['collectionIds']):

					return item['login']['password']
			except KeyError:
				pass

		return None


	def getConfig(self, sect, cfg_def, cfg_ver):

		self.logger.debug(msg0003.format(self.class_name+sect))
		self.config.updateDefaults(self.class_name+sect, cfg_def)
		self.config.updateVerify(cfg_ver)
		self.tasks_cfg = self.config.getSection(self.class_name+sect)

	def getRedknifeConfig(self):

		# defaults
		#config = configparser.ConfigParser(allow_unnamed_section=True)
		config = configparser.ConfigParser()
		config['none'] = {
			'user_filter' : 'Users',
			}
		#	'admin_pw' : '',
		#	'archiv_pw' : '',
		#	'pwd_store' : '',
		#	'pwd_store_pw' : '',

		try:
			#config.read(redknife_cfile)
			with open(redknife_cfile, 'r') as stream:
				config.read_string("[none]\n" + stream.read())
		except FileNotFoundError:
			return None

		except configparser.ParsingError as e:
			self.logger.error(msg0004.format(redknife_cfile, str(e)))
			self.exit(254, '')

		return config


	def getLdapData(self):

		ldap_data = self.gosa_ldap.getAttributeList(self.dn)
		if ldap_data == None:
			self.exit(1, msg0002.format(self.dn))

		return ldap_data


	def loopLdapOC(self, oc):

		dn = self.searchbase
		scope = 'sub'
		search_f = '(&(objectClass={0})(objectClass=sambaIdmapEntry))'.format(oc)
		search_a = ('dn', 'uid', 'cn' )

		ldap_data = self.gosa_ldap.searchLdap(dn, scope, search_f, search_a)

		for data in ldap_data:

			self.dn = data[0]
			try:
				self.cn = data[1]['uid'][0].decode()
			except KeyError:
				self.cn = data[1]['cn'][0].decode()
			self.ldap_data = self.getLdapData()

			if oc == 'posixAccount':

				ret = self.createADuser()
				if ret:
					pwd = self.pprovider(self.cn)
					if pwd:
						self.modifyADpassword(pwd)
					else:
						self.logger.warn(msg0010.format(self.cn))

			elif oc == 'posixGroup':

				self.createADgroup()

			else:
				self.logger.error(msg0017)


	def call(self):

		if not self.s4ad:
			self.logger.error('not configured to handle SambaAD')
			return False

		self.loopLdapOC('posixAccount')
		self.loopLdapOC('posixGroup')

