# Copyright (c) 2024, JINGROW
# For license information, please see license.txt
from __future__ import annotations

import jingrow
from jingrow.model.page import Page

from jcloud.api.client import dashboard_whitelist


class JcloudRole(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

		from jcloud.jcloud.pagetype.jcloud_role_user.jcloud_role_user import JcloudRoleUser

		admin_access: DF.Check
		allow_apps: DF.Check
		allow_bench_creation: DF.Check
		allow_billing: DF.Check
		allow_partner: DF.Check
		allow_server_creation: DF.Check
		allow_site_creation: DF.Check
		allow_webhook_configuration: DF.Check
		team: DF.Link
		title: DF.Data
		users: DF.Table[JcloudRoleUser]
	# end: auto-generated types

	dashboard_fields = (
		"title",
		"users",
		"admin_access",
		"allow_billing",
		"allow_apps",
		"allow_partner",
		"allow_site_creation",
		"allow_bench_creation",
		"allow_server_creation",
		"allow_webhook_configuration",
		"team",
	)

	def before_insert(self):
		if jingrow.db.exists("Jcloud Role", {"title": self.title, "team": self.team}):
			jingrow.throw(f"Role with title {self.title} already exists", jingrow.DuplicateEntryError)

		if not jingrow.local.system_user() and jingrow.session.user != jingrow.db.get_value(
			"Team", self.team, "user"
		):
			jingrow.throw("Only the team owner can create roles")

	def validate(self):
		self.set_first_role_as_admin()
		self.allow_only_one_admin_role()
		self.set_admin_permissions()

	def set_first_role_as_admin(self):
		if not jingrow.get_all("Jcloud Role", filters={"team": self.team}):
			self.admin_access = 1

	def allow_only_one_admin_role(self):
		admin_roles = jingrow.get_all(
			"Jcloud Role",
			filters={"team": self.team, "admin_access": 1, "name": ("!=", self.name)},
		)
		if admin_roles and self.admin_access:
			jingrow.throw("There can only be one admin role per team")

	def set_admin_permissions(self):
		if self.admin_access:
			self.allow_apps = 1
			self.allow_billing = 1
			self.allow_partner = 1
			self.allow_site_creation = 1
			self.allow_bench_creation = 1
			self.allow_server_creation = 1
			self.allow_webhook_configuration = 1

	@dashboard_whitelist()
	def add_user(self, user):
		user_exists = self.get("users", {"user": user})
		if user_exists:
			jingrow.throw(f"{user} already belongs to {self.title}")

		self.append("users", {"user": user})
		self.save()

	@dashboard_whitelist()
	def remove_user(self, user):
		user_exists = self.get("users", {"user": user})
		if not user_exists:
			jingrow.throw(f"{user} does not belong to {self.title}")

		for row in self.users:
			if row.user == user:
				self.remove(row)
				break
		self.save()

	@dashboard_whitelist()
	def delete_permissions(self, permissions: list[str]) -> None:
		for perm in permissions:
			perm_pg = jingrow.get_pg("Jcloud Role Permission", perm)
			if perm_pg.role == self.name:
				perm_pg.delete()

	@dashboard_whitelist()
	def delete(self) -> None:
		if not jingrow.local.system_user() and jingrow.session.user != jingrow.db.get_value(
			"Team", self.team, "user"
		):
			jingrow.throw("Only the team owner can delete this role")

		super().delete()

	def on_trash(self) -> None:
		jingrow.db.delete("Jcloud Role Permission", {"role": self.name})
		jingrow.db.delete("Account Request Jcloud Role", {"jcloud_role": self.name})


def check_role_permissions(pagetype: str, name: str | None = None) -> list[str] | None:  # noqa: C901
	"""
	Check if the user is permitted to access the document based on the role permissions
	Expects the function to throw error for `get` if no permission and return a list of permitted roles for `get_list`
	Note: Empty list means no restrictions

	:param pagetype: Document type
	:param name: Document name
	:return: List of permitted roles or None
	"""
	from jcloud.utils import has_role

	if pagetype not in [
		"Site",
		"Release Group",
		"Server",
		"Marketplace App",
		"Jcloud Webhook",
		"Jcloud Webhook Log",
		"Jcloud Webhook Attempt",
	]:
		return []

	if (hasattr(jingrow.local, "system_user") and jingrow.local.system_user()) or has_role(
		"Jcloud Support Agent"
	):
		return []

	JcloudRoleUser = jingrow.qb.PageType("Jcloud Role User")
	JcloudRole = jingrow.qb.PageType("Jcloud Role")
	query = (
		jingrow.qb.from_(JcloudRole)
		.select(JcloudRole.name)
		.join(JcloudRoleUser)
		.on(JcloudRoleUser.parent == JcloudRole.name)
		.where(JcloudRoleUser.user == jingrow.session.user)
		.where(JcloudRole.team == jingrow.local.team().name)
	)

	if (
		pagetype == "Marketplace App"
		and (roles := query.select(JcloudRole.allow_apps).run(as_dict=1))
		and not any(perm.allow_apps for perm in roles)
	):
		# throw error if any of the roles don't have permission for apps
		jingrow.throw("不允许", jingrow.PermissionError)

	elif (
		pagetype in ["Jcloud Webhook", "Jcloud Webhook Log", "Jcloud Webhook Attempt"]
		and (roles := query.select(JcloudRole.allow_webhook_configuration).run(as_dict=1))
		and not any(perm.allow_webhook_configuration for perm in roles)
	):
		# throw error if any of the roles don't have permission for webhooks
		jingrow.throw("不允许", jingrow.PermissionError)

	elif pagetype in ["Site", "Release Group", "Server"]:
		field = pagetype.lower().replace(" ", "_")
		roles = query.select(JcloudRole.admin_access).run(as_dict=1)

		# this is an admin that can access all sites, release groups, and servers
		if any(perm.admin_access for perm in roles):
			return []

		if roles:
			role_names = [perm.name for perm in roles]
			perms = jingrow.db.get_all(
				"Jcloud Role Permission",
				filters={"role": ["in", role_names], field: name},
			)
			if not perms and name:
				# throw error if the user is not permitted for the document
				jingrow.throw(
					f"You don't have permission to this {pagetype if pagetype != 'Release Group' else 'Bench'}",
					jingrow.PermissionError,
				)
			else:
				return role_names

	return []


def add_permission_for_newly_created_pg(pg: Page) -> None:
	"""
	Used to bulk insert permissions right after a site/release group/server is created
	for users with create permission for respective pagetype is enabled
	"""

	pagetype = pg.pagetype
	if pagetype not in ["Site", "Release Group", "Server"]:
		return

	role_fieldname = ""
	fieldname = pagetype.lower().replace(" ", "_")
	if pagetype == "Site":
		role_fieldname = "allow_site_creation"
	elif pagetype == "Server":
		role_fieldname = "allow_server_creation"
	elif pagetype == "Release Group":
		role_fieldname = "allow_bench_creation"

	new_perms = []
	JcloudRole = jingrow.qb.PageType("Jcloud Role")
	JcloudRoleUser = jingrow.qb.PageType("Jcloud Role User")
	if roles := (
		jingrow.qb.from_(JcloudRole)
		.select(JcloudRole.name)
		.join(JcloudRoleUser)
		.on(JcloudRoleUser.parent == JcloudRole.name)
		.where(JcloudRoleUser.user == jingrow.session.user)
		.where(JcloudRole.team == pg.team)
		.where(JcloudRole[role_fieldname] == 1)
		.run(as_dict=1, pluck="name")
	):
		for role in roles:
			new_perms.append(
				(
					jingrow.generate_hash(length=12),
					role,
					pg.name,
					pg.team,
					jingrow.utils.now(),
					jingrow.utils.now(),
				)
			)

	if new_perms:
		jingrow.db.bulk_insert(
			"Jcloud Role Permission",
			fields=["name", "role", fieldname, "team", "creation", "modified"],
			values=set(new_perms),
		)
