import json
from abc import ABC, abstractmethod
from logging import getLogger
from typing import Dict, List, Union

from django.conf import settings

from NEMO.models import Consumable, Tool
from NEMO.utilities import get_class_from_settings

rates_logger = getLogger(__name__)


class Rates(ABC):

	rates = None

	@abstractmethod
	def load_rates(self, force_reload=False):
		pass

	def get_consumable_rates(self, consumables: List[Consumable]) -> Dict[str, str]:
		if self.rates:
			return {consumable.name : self.get_consumable_rate(consumable) for consumable in consumables}

	@abstractmethod
	def get_consumable_rate(self, consumable: Consumable) -> str:
		pass

	def get_tool_rates(self, tools: List[Tool]) -> Dict[str, str]:
		if self.rates:
			return {tool.name : self.get_tool_rate(tool) for tool in tools}

	@abstractmethod
	def get_tool_rate(self, tool: Tool) -> str:
		pass

	@staticmethod
	def get_expand_rates_table() -> bool:
		from NEMO.views.customization import RatesCustomization
		return RatesCustomization.get_bool("rates_expand_table", raise_exception=False)


class NISTRates(Rates):

	consumable_rate_class = 'inventory_rate'
	tool_rate_class = 'primetime_eq_hourly_rate'
	tool_training_rate_class = 'training_individual_hourly_rate'
	tool_training_group_rate_class = 'training_group_hourly_rate'

	full_cost_rate_class = 'full cost'
	shared_cost_rate_class = 'cost shared'

	def load_rates(self, force_reload=False):
		super().load_rates()
		if force_reload:
			self.rates = None
		if not self.rates:
			json_data = None
			try:
				rates_file = getattr(settings, 'RATES_FILE', settings.MEDIA_ROOT + '/rates.json')
				json_data = open(rates_file)
				self.rates = json.load(json_data)
				rates_logger.info("found rates file and loaded rates")
			except FileNotFoundError as e:
				if hasattr(settings, 'RATES_FILE'):
					rates_logger.exception(e)
				else:
					rates_logger.debug("no rates file, skipping loading rates")
			except Exception as e:
				rates_logger.error("error loading rates")
				rates_logger.exception(e)
			finally:
				if json_data:
					json_data.close()

	def get_consumable_rate(self, consumable) -> str:
		full_cost_rate = self._get_rate_by_table_id_and_class(consumable, self.consumable_rate_class, self.full_cost_rate_class)
		if full_cost_rate:
			return "Cost <b>${:0,.2f}</b>".format(full_cost_rate)

	def get_tool_rate(self, tool: Tool) -> str:
		full_cost_rate = self._get_rate_by_table_id_and_class(tool, self.tool_rate_class, self.full_cost_rate_class)
		shared_cost_rate = self._get_rate_by_table_id_and_class(tool, self.tool_rate_class, self.shared_cost_rate_class)
		if not full_cost_rate and not shared_cost_rate:
			return ""
		training_rate = self._get_rate_by_table_id_and_class(tool, self.tool_training_rate_class, self.full_cost_rate_class)
		training_group_rate = self._get_rate_by_table_id_and_class(tool, self.tool_training_group_rate_class, self.full_cost_rate_class)
		html_rate = f'<div class="media"><a onclick="toggle_details(this)" class="pointer collapsed" data-toggle="collapse" data-target="#rates_details"><span class="glyphicon glyphicon-list-alt pull-left notification-icon primary-highlight"></span><span class="glyphicon pull-left chevron glyphicon-chevron-{"down" if self.get_expand_rates_table() else "right"}"></span></a>'
		html_rate += f'<div class="media-body"><span class="media-heading">Rates</span><div id="rates_details" class="collapse {"in" if self.get_expand_rates_table() else ""}"><table class="table table-bordered table-hover thead-light" style="width: auto !important; min-width: 30%; margin-bottom: 0">'

		table_header = '<tr style="font-size: large">'
		table_header_2 = '<tr style="font-size: x-small">'
		table_row = "<tr>"
		if full_cost_rate:
			table_header += '<th class="text-center" style="padding:15px">Standard Rate</th>'
			table_header_2 += '<th class="text-center" style="padding: 1px;">$/Hour</th>'
			table_row += '<td class="text-center" style="vertical-align: middle">${:0,.2f}</td>'.format(full_cost_rate)
		if shared_cost_rate:
			table_header += '<th class="text-center" style="padding:15px">Reduced Rate</th>'
			table_header_2 += '<th class="text-center" style="padding: 1px;">$/Hour</th>'
			table_row += '<td class="text-center" style="vertical-align: middle">${:0,.2f}</td>'.format(shared_cost_rate)
		if training_rate:
			table_header += '<th class="text-center" style="padding:15px">Individual Training</th>'
			table_header_2 += '<th class="text-center" style="padding: 1px;">$/Hour</th>'
			table_row += '<td class="text-center" style="vertical-align: middle">${:0,.2f}</td>'.format(training_rate)
		if training_group_rate:
			table_header += '<th class="text-center" style="padding:15px">Group Training</th>'
			table_header_2 += '<th class="text-center" style="padding: 1px;">$/Hour</th>'
			table_row += '<td class="text-center" style="vertical-align: middle">${:0,.2f}</td>'.format(training_group_rate)
		table_header += "</tr>"
		table_header_2 += "</tr>"
		table_row += "</tr>"
		html_rate += table_header
		html_rate += table_header_2
		html_rate += table_row
		html_rate += "</tr></table></div></div></div>"
		return html_rate

	def _get_rate_by_table_id_and_class(self, item: Union[Consumable, Tool], table_id, rate_claz) -> float:
		if self.rates:
			matching_rates = list(filter(lambda rate: rate['table_id'] == table_id and rate['rate_class'] == rate_claz and rate['item_id'] == item.id, self.rates))
			if matching_rates:
				return matching_rates[0]['rate']


# ONLY import this LOCALLY to avoid potential issues
rate_class: Rates = get_class_from_settings("RATES_CLASS", "NEMO.rates.NISTRates")