# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe import throw, _
from frappe.model.document import Document
import time
from datetime import timedelta
from frappe.utils import nowdate, get_last_day, getdate, add_days, add_years
from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import make_supplier_scorecard

class SupplierScorecard(Document):

	def validate(self):
		self.validate_standings()
		self.validate_criteria_weights()
		self.calculate_total_score()
		self.update_standing()

	def on_update(self):
		score = make_all_scorecards(self.name)
		if score > 0:
			self.save()

	def validate_standings(self):
		# Check that there are no overlapping scores and check that there are no missing scores
		score = 0
		for c1 in self.standings:
			for c2 in self.standings:
				if c1 != c2:
					if (c1.max_grade > c2.min_grade and c1.min_grade < c2.max_grade):
						throw(_('Overlap in scoring between {0} and {1}').format(c1.standing_name,c2.standing_name))
				if c2.min_grade == score:
					score = c2.max_grade
		if score < 100:
			throw(_('Unable to find score starting at {0}. You need to have standing scores covering 0 to 100').format(score))

	def validate_criteria_weights(self):

		weight = 0
		for c in self.criteria:
			weight += c.weight

		if weight != 100:
			throw(_('Criteria weights must add up to 100%'))

	def calculate_total_score(self):
		scorecards = frappe.db.sql("""
			SELECT
				scp.name
			FROM
				`tabSupplier Scorecard Period` scp
			WHERE
				scp.scorecard = %(sc)s
				AND scp.docstatus = 1
			ORDER BY
				scp.end_date DESC""",
				{"sc": self.name}, as_dict=1)

		period = 0
		total_score = 0
		total_max_score = 0
		for scp in scorecards:
			my_sc = frappe.get_doc('Supplier Scorecard Period', scp.name)
			my_scp_weight = self.weighting_function
			my_scp_weight = my_scp_weight.replace('{period_number}', str(period))

			my_scp_maxweight = my_scp_weight.replace('{total_score}', '100')
			my_scp_weight = my_scp_weight.replace('{total_score}', str(my_sc.total_score))

			max_score = my_sc.calculate_weighted_score(my_scp_maxweight)
			score = my_sc.calculate_weighted_score(my_scp_weight)

			total_score += score
			total_max_score += max_score
			period += 1
		if total_max_score > 0:
			self.supplier_score = round(100.0 * (total_score / total_max_score) ,1)
		else:
			self.supplier_score =  100

	def update_standing(self):
		# Get the setup document

		for standing in self.standings:
			if (not standing.min_grade or (standing.min_grade <= self.supplier_score)) and \
				(not standing.max_grade or (standing.max_grade > self.supplier_score)):
				self.status = standing.standing_name
				self.indicator_color = standing.standing_color
				self.notify_supplier = standing.notify_supplier
				self.notify_employee = standing.notify_employee
				self.employee_link = standing.employee_link

				#Update supplier standing info
				for fieldname in ('prevent_pos', 'prevent_rfqs','warn_rfqs','warn_pos'):
					self.set(fieldname, standing.get(fieldname))
					frappe.db.set_value("Supplier", self.supplier, fieldname, self.get(fieldname))


@frappe.whitelist()
def get_timeline_data(doctype, name):
	# Get a list of all the associated scorecards
	scs = frappe.get_doc(doctype, name)
	out = {}
	timeline_data = {}
	scorecards = frappe.db.sql("""
		SELECT
			sc.name
		FROM
			`tabSupplier Scorecard Period` sc
		WHERE
			sc.scorecard = %(scs)s
			AND sc.docstatus = 1""",
			{"scs": scs.name}, as_dict=1)

	for sc in scorecards:
		start_date, end_date, total_score = frappe.db.get_value('Supplier Scorecard Period', sc.name, ['start_date', 'end_date', 'total_score'])
		for single_date in daterange(start_date, end_date):
			timeline_data[time.mktime(single_date.timetuple())] =  total_score

	out['timeline_data'] = timeline_data
	return out

def daterange(start_date, end_date):
    for n in range(int ((end_date - start_date).days)+1):
        yield start_date + timedelta(n)

def refresh_scorecards():
	scorecards = frappe.db.sql("""
		SELECT
			sc.name
		FROM
			`tabSupplier Scorecard` sc""",
			{}, as_dict=1)
	for sc in scorecards:
		# Check to see if any new scorecard periods are created
		if make_all_scorecards(sc.name) > 0:
			# Save the scorecard to update the score and standings
			frappe.get_doc('Supplier Scorecard', sc.name).save()


@frappe.whitelist()
def make_all_scorecards(docname):

	sc = frappe.get_doc('Supplier Scorecard', docname)
	supplier = frappe.get_doc('Supplier',sc.supplier)

	start_date = getdate(supplier.creation)
	end_date = get_scorecard_date(sc.period, start_date)
	todays = getdate(nowdate())

	scp_count = 0
	first_start_date = todays
	last_end_date = todays

	while (start_date < todays) and (end_date <= todays):
		# check to make sure there is no scorecard period already created
		scorecards = frappe.db.sql("""
			SELECT
				scp.name
			FROM
				`tabSupplier Scorecard Period` scp
			WHERE
				scp.scorecard = %(sc)s
				AND scp.docstatus = 1
				AND (
					(scp.start_date > %(end_date)s
					AND scp.end_date < %(start_date)s)
				OR
					(scp.start_date < %(end_date)s
					AND scp.end_date > %(start_date)s))
			ORDER BY
				scp.end_date DESC""",
				{"sc": docname, "start_date": start_date, "end_date": end_date}, as_dict=1)
		if len(scorecards) == 0:
			period_card = make_supplier_scorecard(docname, None)
			period_card.start_date = start_date
			period_card.end_date = end_date
			period_card.submit()
			scp_count = scp_count + 1
			if start_date < first_start_date:
				first_start_date = start_date
			last_end_date = end_date

		start_date = getdate(add_days(end_date,1))
		end_date = get_scorecard_date(sc.period, start_date)
	if scp_count > 0:
		frappe.msgprint(_("Created {0} scorecards for {1} between: ").format(scp_count, sc.supplier) + str(first_start_date) + " - " + str(last_end_date))
	return scp_count

def get_scorecard_date(period, start_date):
	if period == 'Per Week':
		end_date = getdate(add_days(start_date,7))
	elif period == 'Per Month':
		end_date = get_last_day(start_date)
	elif period == 'Per Year':
		end_date = add_days(add_years(start_date,1), -1)
	return end_date

def make_default_records():
	install_variable_docs = [
		{"param_name": "total_accepted_items", "variable_label": "Total Accepted Items", \
			"path": "get_total_accepted_items"},
		{"param_name": "total_accepted_amount", "variable_label": "Total Accepted Amount", \
			"path": "get_total_accepted_amount"},
		{"param_name": "total_rejected_items", "variable_label": "Total Rejected Items", \
			"path": "get_total_rejected_items"},
		{"param_name": "total_rejected_amount", "variable_label": "Total Rejected Amount", \
			"path": "get_total_rejected_amount"},
		{"param_name": "total_received_items", "variable_label": "Total Received Items", \
			"path": "get_total_received_items"},
		{"param_name": "total_received_amount", "variable_label": "Total Received Amount", \
			"path": "get_total_received_amount"},
		{"param_name": "rfq_response_days", "variable_label": "RFQ Response Days", \
			"path": "get_rfq_response_days"},
		{"param_name": "sq_total_items", "variable_label": "SQ Total Items", \
			"path": "get_sq_total_items"},
		{"param_name": "sq_total_number", "variable_label": "SQ Total Number", \
			"path": "get_sq_total_number"},
		{"param_name": "rfq_total_number", "variable_label": "RFQ Total Number", \
			"path": "get_rfq_total_number"},
		{"param_name": "rfq_total_items", "variable_label": "RFQ Total Items", \
			"path": "get_rfq_total_items"},
		{"param_name": "tot_item_days", "variable_label": "Total Item Days", \
			"path": "get_item_workdays"},
		{"param_name": "on_time_shipment_num", "variable_label": "# of On Time Shipments", "path": \
			"get_on_time_shipments"},
		{"param_name": "cost_of_delayed_shipments", "variable_label": "Cost of Delayed Shipments", \
			"path": "get_cost_of_delayed_shipments"},
		{"param_name": "cost_of_on_time_shipments", "variable_label": "Cost of On Time Shipments", \
			"path": "get_cost_of_on_time_shipments"},
		{"param_name": "total_working_days", "variable_label": "Total Working Days", \
			"path": "get_total_workdays"},
		{"param_name": "tot_cost_shipments", "variable_label": "Total Cost of Shipments", \
			"path": "get_total_cost_of_shipments"},
		{"param_name": "tot_days_late", "variable_label": "Total Days Late", \
			"path": "get_total_days_late"},
		{"param_name": "total_shipments", "variable_label": "Total Shipments", \
			"path": "get_total_shipments"}
	]
	install_standing_docs = [
		{"min_grade": 0.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 30.0, "prevent_pos": 1, \
			"standing_color": "Red", "notify_employee": 0, "standing_name": "Very Poor"},
		{"min_grade": 30.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 50.0, "prevent_pos": 0, \
			"standing_color": "Red", "notify_employee": 0, "standing_name": "Poor"},
		{"min_grade": 50.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 80.0, "prevent_pos": 0, \
			"standing_color": "Green", "notify_employee": 0, "standing_name": "Average"},
		{"min_grade": 80.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 100.0, "prevent_pos": 0, \
			"standing_color": "Blue", "notify_employee": 0, "standing_name": "Excellent"},
	]

	for d in install_variable_docs:
		try:
			d['doctype'] = "Supplier Scorecard Variable"
			frappe.get_doc(d).insert()
		except frappe.NameError:
			pass
	for d in install_standing_docs:
		try:
			d['doctype'] = "Supplier Scorecard Standing"
			frappe.get_doc(d).insert()
		except frappe.NameError:
			pass
