# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE

import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.workflow import DEFAULT_WORKFLOW_TASKS
from frappe.utils import cint


class Workflow(Document):
	# begin: auto-generated types
	# This code is auto-generated. Do not modify anything in this block.

	from typing import TYPE_CHECKING

	if TYPE_CHECKING:
		from frappe.types import DF
		from frappe.workflow.doctype.workflow_document_state.workflow_document_state import (
			WorkflowDocumentState,
		)
		from frappe.workflow.doctype.workflow_transition.workflow_transition import WorkflowTransition

		document_type: DF.Link
		is_active: DF.Check
		override_status: DF.Check
		send_email_alert: DF.Check
		states: DF.Table[WorkflowDocumentState]
		transitions: DF.Table[WorkflowTransition]
		workflow_data: DF.JSON | None
		workflow_name: DF.Data
		workflow_state_field: DF.Data
	# end: auto-generated types

	def validate(self):
		self.set_active()
		self.validate_docstatus()

	def on_update(self):
		self.create_custom_field_for_workflow_state()
		self.update_default_workflow_status()

	def create_custom_field_for_workflow_state(self):
		frappe.clear_cache(doctype=self.document_type)
		meta = frappe.get_meta(self.document_type)
		if not meta.get_field(self.workflow_state_field):
			# create custom field
			frappe.get_doc(
				{
					"doctype": "Custom Field",
					"dt": self.document_type,
					"__islocal": 1,
					"fieldname": self.workflow_state_field,
					"label": self.workflow_state_field.replace("_", " ").title(),
					"hidden": 1,
					"allow_on_submit": 1,
					"no_copy": 1,
					"fieldtype": "Link",
					"options": "Workflow State",
					"owner": "Administrator",
				}
			).save()

			frappe.msgprint(
				_("Created Custom Field {0} in {1}").format(self.workflow_state_field, self.document_type)
			)

	def update_default_workflow_status(self):
		docstatus_map = {}
		states = self.get("states")
		for d in states:
			if d.doc_status not in docstatus_map:
				frappe.db.sql(
					f"""
					UPDATE `tab{self.document_type}`
					SET `{self.workflow_state_field}` = %s
					WHERE ifnull(`{self.workflow_state_field}`, '') = ''
					AND `docstatus` = %s
				""",
					(d.state, d.doc_status),
				)

				docstatus_map[d.doc_status] = d.state

	def validate_docstatus(self):
		def get_state(state):
			for s in self.states:
				if s.state == state:
					return s

			frappe.throw(frappe._("{0} not a valid State").format(state))

		meta = frappe.get_meta(self.document_type)
		is_submittable = meta.is_submittable

		if not is_submittable:
			for state in self.states:
				if cint(state.doc_status) != 0:
					frappe.throw(
						frappe._(
							"Workflow State '{0}' has Document Status {1}, but DocType '{2}' is not submittable. "
							"Only Document Status 0 (Draft) is allowed for non-submittable DocTypes."
						).format(state.state, state.doc_status, self.document_type)
					)

		for t in self.transitions:
			state = get_state(t.state)
			next_state = get_state(t.next_state)
			state_docstatus = cint(state.doc_status)
			next_state_docstatus = cint(next_state.doc_status)

			if state_docstatus == 2:
				frappe.throw(
					frappe._("Cannot change state of Cancelled Document. Transition row {0}").format(t.idx)
				)

			if state_docstatus == 1 and next_state_docstatus == 0:
				frappe.throw(
					frappe._(
						"Submitted Document cannot be converted back to draft. Transition row {0}"
					).format(t.idx)
				)

			if state_docstatus == 0 and next_state_docstatus == 2:
				frappe.throw(frappe._("Cannot cancel before submitting. See Transition {0}").format(t.idx))

	def set_active(self):
		if cint(self.is_active):
			# clear all other
			frappe.db.sql(
				"""UPDATE `tabWorkflow` SET `is_active`=0
				WHERE `document_type`=%s""",
				self.document_type,
			)


@frappe.whitelist()
def get_workflow_state_count(doctype: str, workflow_state_field: str, states: str | list[str]):
	frappe.has_permission(doctype=doctype, ptype="read", throw=True)
	states = frappe.parse_json(states)

	if workflow_state_field in frappe.get_meta(doctype).get_valid_columns():
		result = frappe.get_all(
			doctype,
			fields=[workflow_state_field, {"COUNT": "*", "as": "count"}],
			filters={workflow_state_field: ["not in", states]},
			group_by=workflow_state_field,
		)
		return [r for r in result if r[workflow_state_field]]


@frappe.whitelist(methods=["GET"])
def get_workflow_methods():
	return [i["name"] for i in frappe.get_hooks("workflow_methods")] + DEFAULT_WORKFLOW_TASKS
