# Copyright (c) 2022, JINGROW
# For license information, please see license.txt

import json

import jingrow
from jingrow.model.page import Page


class JcloudJob(Page):
	# 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 jingrow.types import DF

		arguments: DF.Code
		duration: DF.Duration | None
		end: DF.Datetime | None
		job_type: DF.Link
		name: DF.Int | None
		server: DF.DynamicLink | None
		server_type: DF.Link | None
		start: DF.Datetime | None
		status: DF.Literal["Pending", "Running", "Skipped", "Success", "Failure"]
		virtual_machine: DF.Link | None
	# end: auto-generated types

	def before_insert(self):
		jingrow.db.get_value(self.server_type, self.server, "status", for_update=True)
		if existing_jobs := jingrow.db.get_all(
			self.pagetype,
			{
				"status": ("in", ["Pending", "Running"]),
				"server_type": self.server_type,
				"server": self.server,
			},
			["job_type", "status"],
		):
			jingrow.throw(
				f"A {existing_jobs[0].job_type} job is already {existing_jobs[0].status}. Please wait for the same."
			)

	def after_insert(self):
		self.create_jcloud_job_steps()
		self.execute()

	def on_update(self):
		self.publish_update()

	def create_jcloud_job_steps(self):
		job_type = jingrow.get_pg("Jcloud Job Type", self.job_type)
		for step in job_type.steps:
			pg = jingrow.get_pg(
				{
					"pagetype": "Jcloud Job Step",
					"job": self.name,
					"status": "Pending",
					"job_type": self.job_type,
					"step_name": step.step_name,
					"wait_until_true": step.wait_until_true,
				}
			)
			pg.insert()

	def execute(self):
		self.status = "Running"
		self.start = jingrow.utils.now_datetime()
		self.save()
		self.next()

	def fail(self, arguments=None):
		self.status = "Failure"
		pending_steps = jingrow.get_all(
			"Jcloud Job Step", {"job": self.name, "status": "Pending"}
		)
		for step in pending_steps:
			jingrow.db.set_value("Jcloud Job Step", step.name, "status", "Skipped")
		self.end = jingrow.utils.now_datetime()
		self.duration = (self.end - self.start).total_seconds()
		self.save()

	def succeed(self):
		self.status = "Success"
		self.end = jingrow.utils.now_datetime()
		self.duration = (self.end - self.start).total_seconds()
		self.save()

	@jingrow.whitelist()
	def next(self, arguments=None):
		if arguments:
			old_arguments = json.loads(self.arguments)
			old_arguments.update(arguments)
			self.arguments = json.dumps(old_arguments, indent=2)
		self.status = "Running"
		self.save()
		next_step = self.next_step

		if not next_step:
			self.succeed()
			return

		jingrow.enqueue_pg("Jcloud Job Step", next_step, "execute", enqueue_after_commit=True)

	@jingrow.whitelist()
	def force_continue(self):
		for step in jingrow.get_all(
			"Jcloud Job Step",
			{"job": self.name, "status": ("in", ("Failure", "Skipped"))},
			pluck="name",
		):
			jingrow.db.set_value("Jcloud Job Step", step, "status", "Pending")
		self.next()

	@jingrow.whitelist()
	def force_fail(self):
		for step in jingrow.get_all(
			"Jcloud Job Step",
			{"job": self.name, "status": "Pending"},
			pluck="name",
		):
			jingrow.db.set_value("Jcloud Job Step", step, "status", "Failure")
		jingrow.db.set_value("Jcloud Job", self.name, "status", "Failure")

	@property
	def next_step(self):
		return jingrow.db.get_value(
			"Jcloud Job Step",
			{"job": self.name, "status": "Pending"},
			"name",
			order_by="name asc",
			as_dict=True,
		)

	def detail(self):
		steps = jingrow.get_all(
			"Jcloud Job Step",
			filters={"job": self.name},
			fields=["name", "step_name", "status", "start", "end", "duration"],
			order_by="name asc",
		)

		for index, step in enumerate(steps):
			if step.status == "Pending" and index and steps[index - 1].status == "Success":
				step.status = "Running"

		return {
			"name": self.name,
			"job_type": self.job_type,
			"server": self.server,
			"server_type": self.server_type,
			"virtual_machine": self.virtual_machine,
			"status": self.status,
			"steps": steps,
		}

	def publish_update(self):
		jingrow.publish_realtime(
			"jcloud_job_update", pagetype=self.pagetype, docname=self.name, message=self.detail()
		)

	def on_trash(self):
		jingrow.db.delete("Jcloud Job Step", {"job": self.name})
