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

import json
from datetime import datetime

import jingrow
from jingrow.model.page import Page
from jingrow.utils import add_to_date

from jcloud.utils import log_error


class SaasAppSubscription(Page):
	def validate(self):
		self.set_secret_key()
		self.validate_saas_app_plan()
		self.set_plan()

	def set_secret_key(self):
		if not self.secret_key:
			self.secret_key = jingrow.generate_hash(length=40)
			self.create_site_config_key()

	def create_site_config_key(self):
		if not jingrow.db.exists("Site Config Key", {"key": f"sk_{self.app}"}):
			jingrow.get_pg(
				pagetype="Site Config Key", internal=True, key=f"sk_{self.app}"
			).insert(ignore_permissions=True)

	def before_insert(self):
		self.validate_duplicate_subscription()

	def after_insert(self):
		self.set_secret_key_in_site_config()

	def set_secret_key_in_site_config(self):
		site_pg = jingrow.get_pg("Site", self.site)

		new_config = [
			{"key": f"sk_{self.app}", "value": self.secret_key, "type": "String"},
		]

		if "email_delivery_service" in [
			_app["app"] for _app in jingrow.get_pg("Saas Settings", self.app).as_dict()["apps"]
		]:
			new_config.extend(
				[
					{"key": "sk_email_delivery_service", "value": self.secret_key},
					{"key": "mail_login", "value": "example@gmail.com", "type": "String"},
					{"key": "mail_password", "value": "password", "type": "String"},
					{"key": "mail_port", "value": 587, "type": "Number"},
					{"key": "mail_server", "value": "smtp.gmail.com", "type": "String"},
				]
			)

		if self.app == "jerp_smb":
			new_config.append({"key": "plan", "value": self.initial_plan or "Free"})

		site_pg.update_site_config(new_config)

	def validate_saas_app_plan(self):
		app = jingrow.db.get_value("Saas App Plan", self.saas_app_plan, "app")

		if app != self.app:
			jingrow.throw(f"Plan {self.saas_app_plan} is not for app {jingrow.bold(self.app)}!")

	def set_plan(self):
		self.plan, self.site_plan = jingrow.db.get_value(
			"Saas App Plan", self.saas_app_plan, ["plan", "site_plan"]
		)

	def change_plan(self, new_plan, ignore_card_setup=False):
		self.saas_app_plan = new_plan
		self.status = "Active"
		self.save(ignore_permissions=True)

		site_pg = jingrow.get_pg("Site", self.site)
		site_pg.change_plan(self.site_plan, ignore_card_setup)

		# TODO: Remove this from here
		if self.app == "jerp_smb":
			site = jingrow.get_pg("Site", self.site)
			config = json.loads(
				jingrow.db.get_value("Saas App Plan", self.saas_app_plan, "config")
			)
			site.update_site_config({"plan": config["plan"]})

	def update_end_date(self, payment_option):
		days = 364 if payment_option == "Annual" else 29
		self.end_date = add_to_date(self.end_date or datetime.today().date(), days=days)

	def validate_duplicate_subscription(self):
		already_exists = jingrow.db.exists(
			"Saas App Subscription", {"app": self.app, "site": self.site}
		)

		if already_exists:
			jingrow.throw(
				f"Subscription for app '{jingrow.bold(self.app)}' already exists for"
				f" site '{jingrow.bold(self.site)}'!"
			)

	def deactivate(self):
		self.status = "Inactive"
		self.save(ignore_permissions=True)
		jingrow.get_pg("Site", self.site).deactivate()

	def activate(self):
		self.status = "Active"
		self.save(ignore_permissions=True)
		jingrow.get_pg("Site", self.site).activate()

	def suspend(self):
		self.status = "Suspended"
		self.save(ignore_permissions=True)
		jingrow.get_pg("Site", self.site).suspend(skip_reload=True)

	def disable(self):
		if self.status == "Disabled":
			return
		self.status = "Disabled"
		self.save(ignore_permissions=True)

	def calculate_payout(self, amount, saas_app_plan=None):
		# Amount of money that is supposed to be paidout to the developers
		saas_app_plan = saas_app_plan or self.saas_app_plan
		payout_percentage = jingrow.db.get_value(
			"Saas App Plan", saas_app_plan, "payout_percentage"
		)
		return (amount / 100) * float(payout_percentage)

	def create_usage_record(self):
		if self.is_usage_record_created():
			return

		team_name = jingrow.db.get_value("Site", self.site, "team")
		team = jingrow.get_cached_pg("Team", team_name)

		if team.parent_team:
			team = jingrow.get_cached_pg("Team", team.parent_team)

		if team.billing_team:
			team = jingrow.get_cached_pg("Team", team.billing_team)

		if not team.get_upcoming_invoice():
			team.create_upcoming_invoice()

		plan = jingrow.get_cached_pg("Site Plan", self.plan)
		amount = plan.get_price_for_interval(self.interval, team.currency)
		payout = self.calculate_payout(amount)

		usage_record = jingrow.get_pg(
			pagetype="Usage Record",
			team=team.name,
			document_type="Saas App",
			document_name=self.app,
			plan=self.plan,
			amount=amount,
			subscription=self.name,
			interval=self.interval,
			payout=payout,
		)
		usage_record.insert()
		usage_record.submit()
		return usage_record

	def is_usage_record_created(self):
		team = jingrow.db.get_value("Site", self.site, "team")
		filters = {
			"team": team,
			"document_type": "Saas App",
			"document_name": self.app,
			"subscription": self.name,
			"interval": self.interval,
			"plan": self.plan,
		}

		if self.interval == "Daily":
			filters.update({"date": jingrow.utils.today()})

		if self.interval == "Monthly":
			date = jingrow.utils.getdate()
			first_day = jingrow.utils.get_first_day(date)
			last_day = jingrow.utils.get_last_day(date)
			filters.update({"date": ("between", (first_day, last_day))})

		result = jingrow.db.get_all("Usage Record", filters=filters, limit=1)
		return bool(result)


def suspend_prepaid_subscriptions():
	filters = [
		["end_date", "<", jingrow.utils.today()],
		["end_date", "is", "set"],
		["status", "=", "Active"],
	]
	subscriptions = jingrow.get_all("Saas App Subscription", filters=filters)
	for sub in subscriptions:
		subscription = jingrow.get_pg("Saas App Subscription", sub.name)
		try:
			subscription.suspend()
			jingrow.db.commit()
		except Exception:
			jingrow.db.rollback(
				"Saas Subscription: Cannot suspend prepaid subscription", subscription.name
			)


def create_usage_records():
	subscriptions = jingrow.db.get_all(
		"Saas App Subscription",
		filters={"status": "Active", "end_date": ["is", "not set"]},
		pluck="name",
	)
	for sub in subscriptions:
		subscription = jingrow.get_pg("Saas App Subscription", sub)

		if not should_create_usage_record(subscription):
			continue

		try:
			subscription.create_usage_record()
			jingrow.db.commit()
		except Exception:
			jingrow.db.rollback()
			log_error(title="Saas App: Create Usage Record Error", name=sub)


def should_create_usage_record(subscription):
	# Don't create for free plans
	is_free = jingrow.db.get_value("Saas App Plan", subscription.saas_app_plan, "is_free")

	if is_free:
		return False

	return True


def process_prepaid_saas_payment(event):
	from datetime import datetime

	payment_intent = event["data"]["object"]
	team = jingrow.get_pg("Team", {"stripe_customer_id": payment_intent["customer"]})
	amount = payment_intent["amount"] / 100
	metadata = payment_intent.get("metadata")
	saas_plan = metadata.get("plan")
	payment_option = metadata.get("payment_option")

	# change subscription
	sub = jingrow.get_pg("Saas App Subscription", metadata.get("subscription"))
	sub.interval = payment_option
	sub.update_end_date(payment_option)
	sub.change_plan(saas_plan, True)
	sub.reload()

	# create invoice
	payout = sub.calculate_payout(amount)
	due_date = datetime.fromtimestamp(payment_intent["created"])
	payment_id = payment_intent["id"]
	document_name = metadata.get("app")
	invoice = create_saas_invoice(
		team=team,
		amount=amount,
		payout=payout,
		document_name=document_name,
		due_date=due_date,
		plan=jingrow.db.get_value("Saas App Plan", saas_plan, "plan"),
		payment_id=payment_id,
	)

	# there should only be one charge object
	charge = payment_intent["charges"]["data"][0]["id"]
	# update transaction amount, fee and exchange rate
	invoice.update_transaction_details(charge)
	invoice.submit()


def create_saas_invoice(
	team,
	due_date,
	amount,
	payout,
	document_name,
	plan,
	payment_id=None,
	status="Paid",
):
	invoice = jingrow.get_pg(
		pagetype="Invoice",
		team=team.name,
		type="Service",
		status=status,
		due_date=due_date,
		amount_paid=amount,
		amount_due=amount,
		stripe_payment_intent_id=payment_id,
		payout=payout,
	)
	invoice.append(
		"items",
		{
			"description": "Saas Prepaid Purchase",
			"document_type": "Saas App",
			"document_name": document_name,
			"plan": jingrow.db.get_value("Site Plan", plan, "plan_title"),
			"quantity": 1,
			"rate": amount,
		},
	)
	invoice.insert()
	invoice.reload()

	return invoice
