# Copyright (c) 2018, Xhive LLC
# For license information, please see license.txt


import xhiveframework
from xhiveframework import _
from xhiveframework.model.document import Document
from xhiveframework.utils import add_days, date_diff, format_date, get_link_to_form, getdate

from xhiveerp.setup.doctype.employee.employee import is_holiday

from hrms.hr.utils import validate_active_employee, validate_dates


class OverlappingAttendanceRequestError(xhiveframework.ValidationError):
	pass


class AttendanceRequest(Document):
	def validate(self):
		validate_active_employee(self.employee)
		validate_dates(self, self.from_date, self.to_date)
		self.validate_half_day()
		self.validate_request_overlap()

	def validate_half_day(self):
		if self.half_day:
			if not getdate(self.from_date) <= getdate(self.half_day_date) <= getdate(self.to_date):
				xhiveframework.throw(_("Half day date should be in between from date and to date"))

	def validate_request_overlap(self):
		if not self.name:
			self.name = "New Attendance Request"

		Request = xhiveframework.qb.DocType("Attendance Request")
		overlapping_request = (
			xhiveframework.qb.from_(Request)
			.select(Request.name)
			.where(
				(Request.employee == self.employee)
				& (Request.docstatus < 2)
				& (Request.name != self.name)
				& (self.to_date >= Request.from_date)
				& (self.from_date <= Request.to_date)
			)
		).run(as_dict=True)

		if overlapping_request:
			self.throw_overlap_error(overlapping_request[0].name)

	def throw_overlap_error(self, overlapping_request: str):
		msg = _(
			"Employee {0} already has an Attendance Request {1} that overlaps with this period"
		).format(
			xhiveframework.bold(self.employee),
			get_link_to_form("Attendance Request", overlapping_request),
		)

		xhiveframework.throw(
			msg, title=_("Overlapping Attendance Request"), exc=OverlappingAttendanceRequestError
		)

	def on_submit(self):
		self.create_attendance_records()

	def on_cancel(self):
		attendance_list = xhiveframework.get_all(
			"Attendance", {"employee": self.employee, "attendance_request": self.name, "docstatus": 1}
		)
		if attendance_list:
			for attendance in attendance_list:
				attendance_obj = xhiveframework.get_doc("Attendance", attendance["name"])
				attendance_obj.cancel()

	def create_attendance_records(self):
		request_days = date_diff(self.to_date, self.from_date) + 1
		for day in range(request_days):
			attendance_date = add_days(self.from_date, day)
			if self.should_mark_attendance(attendance_date):
				self.create_or_update_attendance(attendance_date)

	def create_or_update_attendance(self, date: str):
		attendance_name = self.get_attendance_record(date)
		status = self.get_attendance_status(date)

		if attendance_name:
			# update existing attendance, change the status
			doc = xhiveframework.get_doc("Attendance", attendance_name)
			old_status = doc.status

			if old_status != status:
				doc.db_set({"status": status, "attendance_request": self.name})
				text = _("changed the status from {0} to {1} via Attendance Request").format(
					xhiveframework.bold(old_status), xhiveframework.bold(status)
				)
				doc.add_comment(comment_type="Info", text=text)

				xhiveframework.msgprint(
					_("Updated status from {0} to {1} for date {2} in the attendance record {3}").format(
						xhiveframework.bold(old_status),
						xhiveframework.bold(status),
						xhiveframework.bold(format_date(date)),
						get_link_to_form("Attendance", doc.name),
					),
					title=_("Attendance Updated"),
				)
		else:
			# submit a new attendance record
			doc = xhiveframework.new_doc("Attendance")
			doc.employee = self.employee
			doc.attendance_date = date
			doc.company = self.company
			doc.attendance_request = self.name
			doc.status = status
			doc.insert(ignore_permissions=True)
			doc.submit()

	def should_mark_attendance(self, attendance_date: str) -> bool:
		# Check if attendance_date is a holiday
		if is_holiday(self.employee, attendance_date):
			xhiveframework.msgprint(
				_("Attendance not submitted for {0} as it is a Holiday.").format(
					xhiveframework.bold(format_date(attendance_date))
				)
			)
			return False

		# Check if employee is on leave
		if self.has_leave_record(attendance_date):
			xhiveframework.msgprint(
				_("Attendance not submitted for {0} as {1} is on leave.").format(
					xhiveframework.bold(format_date(attendance_date)), xhiveframework.bold(self.employee)
				)
			)
			return False

		return True

	def has_leave_record(self, attendance_date: str) -> str | None:
		return xhiveframework.db.exists(
			"Leave Application",
			{
				"employee": self.employee,
				"docstatus": 1,
				"from_date": ("<=", attendance_date),
				"to_date": (">=", attendance_date),
			},
		)

	def get_attendance_record(self, attendance_date: str) -> str | None:
		return xhiveframework.db.exists(
			"Attendance",
			{
				"employee": self.employee,
				"attendance_date": attendance_date,
				"docstatus": ("!=", 2),
			},
		)

	def get_attendance_status(self, attendance_date: str) -> str:
		if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0:
			return "Half Day"
		elif self.reason == "Work From Home":
			return "Work From Home"
		else:
			return "Present"

	@xhiveframework.whitelist()
	def get_attendance_warnings(self) -> list:
		attendance_warnings = []
		request_days = date_diff(self.to_date, self.from_date) + 1

		for day in range(request_days):
			attendance_date = add_days(self.from_date, day)

			if is_holiday(self.employee, attendance_date):
				attendance_warnings.append({"date": attendance_date, "reason": "Holiday", "action": "Skip"})
			elif self.has_leave_record(attendance_date):
				attendance_warnings.append({"date": attendance_date, "reason": "On Leave", "action": "Skip"})
			else:
				attendance = self.get_attendance_record(attendance_date)
				if attendance:
					attendance_warnings.append(
						{
							"date": attendance_date,
							"reason": "Attendance already marked",
							"record": attendance,
							"action": "Overwrite",
						}
					)

		return attendance_warnings
