import json

import pika
from flask import render_template, request

import modules.db.sql as sql
import modules.common.common as common
import modules.roxywi.common as roxywi_common

error_mess = common.error_mess


def send_message_to_rabbit(message: str, **kwargs) -> None:
	rabbit_user = sql.get_setting('rabbitmq_user')
	rabbit_password = sql.get_setting('rabbitmq_password')
	rabbit_host = sql.get_setting('rabbitmq_host')
	rabbit_port = sql.get_setting('rabbitmq_port')
	rabbit_vhost = sql.get_setting('rabbitmq_vhost')
	if kwargs.get('rabbit_queue'):
		rabbit_queue = kwargs.get('rabbit_queue')
	else:
		rabbit_queue = sql.get_setting('rabbitmq_queue')

	credentials = pika.PlainCredentials(rabbit_user, rabbit_password)
	parameters = pika.ConnectionParameters(
		rabbit_host,
		rabbit_port,
		rabbit_vhost,
		credentials
	)

	connection = pika.BlockingConnection(parameters)
	channel = connection.channel()
	channel.queue_declare(queue=rabbit_queue)
	channel.basic_publish(exchange='', routing_key=rabbit_queue, body=message)

	connection.close()


def alert_routing(
	server_ip: str, service_id: int, group_id: int, level: str, mes: str, alert_type: str
) -> None:
	subject: str = level + ': ' + mes
	server_id: int = sql.select_server_id_by_ip(server_ip)
	checker_settings = sql.select_checker_settings_for_server(service_id, server_id)

	try:
		json_for_sending = {"user_group": group_id, "message": subject}
		send_message_to_rabbit(json.dumps(json_for_sending))
	except Exception as e:
		roxywi_common.logging('Roxy-WI server', f'error: unable to send message: {e}', roxywi=1)

	for setting in checker_settings:
		if alert_type == 'service' and setting.service_alert:
			try:
				telegram_send_mess(mes, level, channel_id=setting.telegram_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to Telegram: {e}', roxywi=1)
			try:
				slack_send_mess(mes, level, channel_id=setting.slack_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to Slack: {e}', roxywi=1)
			try:
				pd_send_mess(mes, level, server_ip, service_id, alert_type, channel_id=setting.pd_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to PagerDuty: {e}', roxywi=1)

			if setting.email:
				send_email_to_server_group(subject, mes, level, group_id)

		if alert_type == 'backend' and setting.backend_alert:
			try:
				telegram_send_mess(mes, level, channel_id=setting.telegram_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to Telegram: {e}', roxywi=1)
			try:
				slack_send_mess(mes, level, channel_id=setting.slack_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to Slack: {e}', roxywi=1)
			try:
				pd_send_mess(mes, level, server_ip, service_id, alert_type, channel_id=setting.pd_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to PagerDuty: {e}', roxywi=1)

			if setting.email:
				send_email_to_server_group(subject, mes, level, group_id)

		if alert_type == 'maxconn' and setting.maxconn_alert:
			try:
				telegram_send_mess(mes, level, channel_id=setting.telegram_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to Telegram: {e}', roxywi=1)
			try:
				slack_send_mess(mes, level, channel_id=setting.slack_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to Slack: {e}', roxywi=1)
			try:
				pd_send_mess(mes, level, server_ip, service_id, alert_type, channel_id=setting.pd_id)
			except Exception as e:
				roxywi_common.logging('Roxy-WI server', f'error: unable to send message to PagerDuty: {e}', roxywi=1)

			if setting.email:
				send_email_to_server_group(subject, mes, level, group_id)


def send_email_to_server_group(subject: str, mes: str, level: str, group_id: int) -> None:
	try:
		users_email = sql.select_users_emails_by_group_id(group_id)

		for user_email in users_email:
			send_email(user_email.email, subject, f'{level}: {mes}')
	except Exception as e:
		roxywi_common.logging('Roxy-WI server', f'error: unable to send email: {e}', roxywi=1)


def send_email(email_to: str, subject: str, message: str) -> None:
	from smtplib import SMTP

	try:
		from email.MIMEText import MIMEText
	except Exception:
		from email.mime.text import MIMEText

	mail_ssl = sql.get_setting('mail_ssl')
	mail_from = sql.get_setting('mail_from')
	mail_smtp_host = sql.get_setting('mail_smtp_host')
	mail_smtp_port = sql.get_setting('mail_smtp_port')
	mail_smtp_user = sql.get_setting('mail_smtp_user')
	mail_smtp_password = sql.get_setting('mail_smtp_password').replace("'", "")

	msg = MIMEText(message)
	msg['Subject'] = f'Roxy-WI: {subject}'
	msg['From'] = f'Roxy-WI <{mail_from}>'
	msg['To'] = email_to

	try:
		smtp_obj = SMTP(mail_smtp_host, mail_smtp_port)
		if mail_ssl:
			smtp_obj.starttls()
		smtp_obj.login(mail_smtp_user, mail_smtp_password)
		smtp_obj.send_message(msg)
		roxywi_common.logging('Roxy-WI server', f'An email has been sent to {email_to}', roxywi=1)
	except Exception as e:
		roxywi_common.logging('Roxy-WI server', f'error: unable to send email: {e}', roxywi=1)


def telegram_send_mess(mess, level, **kwargs):
	import telebot
	from telebot import apihelper
	token_bot = ''
	channel_name = ''

	if kwargs.get('channel_id') == 0:
		return

	if kwargs.get('channel_id'):
		telegrams = sql.get_telegram_by_id(kwargs.get('channel_id'))
	else:
		telegrams = sql.get_telegram_by_ip(kwargs.get('ip'))

	proxy = sql.get_setting('proxy')

	for telegram in telegrams:
		token_bot = telegram.token
		channel_name = telegram.chanel_name

	if token_bot == '' or channel_name == '':
		mess = " Can't send message. Add Telegram channel before use alerting at this servers group"
		roxywi_common.logging('Roxy-WI server', mess, roxywi=1)

	if proxy is not None and proxy != '' and proxy != 'None':
		apihelper.proxy = {'https': proxy}
	try:
		bot = telebot.TeleBot(token=token_bot)
		bot.send_message(chat_id=channel_name, text=f'{level}: {mess}')
		return 'ok'
	except Exception as e:
		roxywi_common.logging('Roxy-WI server', str(e), roxywi=1)
		raise Exception(f'error: {e}')


def slack_send_mess(mess, level, **kwargs):
	from slack_sdk import WebClient
	from slack_sdk.errors import SlackApiError
	slack_token = ''
	channel_name = ''

	if kwargs.get('channel_id') == 0:
		return

	if kwargs.get('channel_id'):
		slacks = sql.get_slack_by_id(kwargs.get('channel_id'))
	else:
		slacks = sql.get_slack_by_ip(kwargs.get('ip'))

	proxy = sql.get_setting('proxy')

	for slack in slacks:
		slack_token = slack.token
		channel_name = slack.chanel_name

	if proxy is not None and proxy != '' and proxy != 'None':
		proxies = dict(https=proxy, http=proxy)
		client = WebClient(token=slack_token, proxies=proxies)
	else:
		client = WebClient(token=slack_token)

	try:
		client.chat_postMessage(channel=f'#{channel_name}', text=f'{level}: {mess}')
		return 'ok'
	except SlackApiError as e:
		roxywi_common.logging('Roxy-WI server', str(e), roxywi=1)
		raise Exception(f'error: {e}')


def pd_send_mess(mess, level, server_ip=None, service_id=None, alert_type=None, **kwargs):
	import pdpyras

	token = ''

	if kwargs.get('channel_id') == 0:
		return

	if kwargs.get('channel_id'):
		try:
			pds = sql.get_pd_by_id(kwargs.get('channel_id'))
		except Exception as e:
			print(e)
	else:
		try:
			pds = sql.get_pd_by_ip(kwargs.get('ip'))
		except Exception as e:
			print(e)

	for pd in pds:
		token = pd.token

	try:
		proxy = sql.get_setting('proxy')
		session = pdpyras.EventsAPISession(token)
		dedup_key = f'{server_ip} {service_id} {alert_type}'
	except Exception as e:
		roxywi_common.logging('Roxy-WI server', str(e), roxywi=1)
		raise Exception(f'error: {e}')

	if proxy is not None and proxy != '' and proxy != 'None':
		proxies = dict(https=proxy, http=proxy)
		session.proxies.update(proxies)

	try:
		if level == 'info':
			session.resolve(dedup_key)
		else:
			session.trigger(mess, 'Roxy-WI', dedup_key=dedup_key, severity=level, custom_details={'server': server_ip, 'alert': mess})
		return 'ok'
	except Exception as e:
		roxywi_common.logging('Roxy-WI server', str(e), roxywi=1)
		raise Exception(f'error: {e}')


def check_rabbit_alert() -> str:
	try:
		user_group_id = request.cookies.get('group')
	except Exception as e:
		return f'error: Cannot send a message {e}'

	try:
		json_for_sending = {"user_group": user_group_id, "message": 'info: Test message'}
		send_message_to_rabbit(json.dumps(json_for_sending))
	except Exception as e:
		return f'error: Cannot send a message {e}'
	else:
		return 'ok'


def check_email_alert() -> str:
	subject = 'test message'
	message = 'Test message from Roxy-WI'

	try:
		user_uuid = request.cookies.get('uuid')
	except Exception as e:
		return f'error: Cannot send a message {e}'

	try:
		user_email = sql.select_user_email_by_uuid(user_uuid)
	except Exception as e:
		return f'error: Cannot get a user email: {e}'

	try:
		send_email(user_email, subject, message)
	except Exception as e:
		return f'error: Cannot send a message {e}'

	return 'ok'


def add_telegram_channel(token: str, channel: str, group: str, page: str) -> str:
	if token is None or channel is None or group is None:
		return error_mess
	else:
		if sql.insert_new_telegram(token, channel, group):
			lang = roxywi_common.get_user_lang_for_flask()
			channels = sql.select_telegram(token=token)
			groups = sql.select_groups()
			roxywi_common.logging('Roxy-WI server', f'A new Telegram channel {channel} has been created ', roxywi=1, login=1)

			return render_template('ajax/new_receiver.html', groups=groups, lang=lang, channels=channels, page=page, receiver='telegram')


def add_slack_channel(token: str, channel: str, group: str, page: str) -> str:
	if token is None or channel is None or group is None:
		return error_mess
	else:
		if sql.insert_new_slack(token, channel, group):
			lang = roxywi_common.get_user_lang_for_flask()
			channels = sql.select_slack(token=token)
			groups = sql.select_groups()
			roxywi_common.logging('Roxy-WI server', f'A new Slack channel {channel} has been created ', roxywi=1, login=1)
			return render_template('ajax/new_receiver.html', groups=groups, lang=lang, channels=channels, page=page, receiver='slack')


def add_pd_channel(token: str, channel: str, group: str, page: str) -> str:
	if token is None or channel is None or group is None:
		return error_mess
	else:
		if sql.insert_new_pd(token, channel, group):
			lang = roxywi_common.get_user_lang_for_flask()
			channels = sql.select_slack(token=token)
			groups = sql.select_groups()
			roxywi_common.logging('Roxy-WI server', f'A new PagerDuty channel {channel} has been created ', roxywi=1, login=1)
			return render_template('ajax/new_receiver.html', groups=groups, lang=lang, channels=channels, page=page, receiver='pd')


def delete_telegram_channel(channel_id) -> str:
	telegram = sql.select_telegram(id=channel_id)
	channel_name = ''
	for t in telegram:
		channel_name = t.token
	if sql.delete_telegram(channel_id):
		roxywi_common.logging('Roxy-WI server', f'The Telegram channel {channel_name} has been deleted ', roxywi=1, login=1)
		return 'ok'


def delete_slack_channel(channel_id) -> str:
	slack = sql.select_slack(id=channel_id)
	channel_name = ''
	for t in slack:
		channel_name = t.chanel_name
	if sql.delete_slack(channel_id):
		roxywi_common.logging('Roxy-WI server', f'The Slack channel {channel_name} has been deleted ', roxywi=1, login=1)
		return 'ok'


def delete_pd_channel(channel_id) -> str:
	pd = sql.select_pd(id=channel_id)
	channel_name = ''
	for t in pd:
		channel_name = t.chanel_name
	if sql.delete_pd(channel_id):
		roxywi_common.logging('Roxy-WI server', f'The PageDuty channel {channel_name} has been deleted ', roxywi=1, login=1)
		return 'ok'


def update_telegram(token: str, channel: str, group: str, user_id: int) -> str:
	sql.update_telegram(token, channel, group, user_id)
	roxywi_common.logging('group ' + group, f'The Telegram token has been updated for channel: {channel}', roxywi=1, login=1)
	return 'ok'


def update_slack(token: str, channel: str, group: str, user_id: int) -> str:
	sql.update_slack(token, channel, group, user_id)
	roxywi_common.logging(f'group {group}', f'The Slack token has been updated for channel: {channel}', roxywi=1, login=1)
	return 'ok'


def update_pd(token: str, channel: str, group: str, user_id: int) -> str:
	sql.update_pd(token, channel, group, user_id)
	roxywi_common.logging(f'group {group}', f'The PagerDuty token has been updated for channel: {channel}', roxywi=1, login=1)
	return 'ok'


def delete_receiver_channel(channel_id: int, receiver_name: str) -> None:
	delete_functions = {
		"telegram": delete_telegram_channel,
		"slack": delete_slack_channel,
		"pd": delete_pd_channel,
	}
	return delete_functions[receiver_name](channel_id)


def add_receiver_channel(receiver_name: str, token: str, channel: str, group: id, page: str) -> str:
	add_functions = {
		"telegram": add_telegram_channel,
		"slack": add_slack_channel,
		"pd": add_pd_channel,
	}

	try:
		return add_functions[receiver_name](token, channel, group, page)
	except Exception as e:
		return f'error: Cannot add new receiver: {e}'


def update_receiver_channel(receiver_name: str, token: str, channel: str, group: id, user_id: int) -> None:
	update_functions = {
		"telegram": update_telegram,
		"slack": update_slack,
		"pd": update_pd,
	}
	return update_functions[receiver_name](token, channel, group, user_id)


def check_receiver(channel_id: int, receiver_name: str) -> str:
	functions = {
		"telegram": telegram_send_mess,
		"slack": slack_send_mess,
		"pd": pd_send_mess,
	}
	mess = 'Test message from Roxy-WI'

	if receiver_name == 'pd':
		level = 'warning'
	else:
		level = 'info'

	try:
		return functions[receiver_name](mess, level, channel_id=channel_id)
	except Exception as e:
		return f'error: Cannot send message: {e}'
