from typing import Dict, List

import jingrow
from jingrow.utils import get_url

from jcloud.api.developer import raise_invalid_key_error
from jcloud.api.site import get_plans as get_site_plans
from jcloud.utils.telemetry import capture


class DeveloperApiHandler:
	def __init__(self, secret_key: str) -> None:
		self.secret_key = secret_key
		self.validate_secret_key()

	def validate_secret_key(self):
		"""Validate secret_key and set app subscription name and pg"""

		if not self.secret_key or not isinstance(self.secret_key, str):
			raise_invalid_key_error()

		app_subscription_name = jingrow.db.exists(
			"Subscription", {"secret_key": self.secret_key, "enabled": 1}
		)

		if not app_subscription_name:
			raise_invalid_key_error()

		self.app_subscription_name = app_subscription_name
		self.set_subscription_pg()

	def set_subscription_pg(self):
		"""To be called after `secret_key` validation"""
		self.app_subscription_pg = jingrow.get_pg("Subscription", self.app_subscription_name)

	def get_subscription_status(self) -> str:
		return self.app_subscription_pg.status

	def get_subscription_info(self) -> Dict:
		"""Important rule for security: Send info back carefully"""
		app_subscription_dict = self.app_subscription_pg.as_dict()
		fields_to_send = [
			"document_name",
			"enabled",
			"plan",
			"site",
		]

		filtered_dict = {
			x: app_subscription_dict[x] for x in app_subscription_dict if x in fields_to_send
		}

		return filtered_dict

	def get_subscription(self) -> Dict:
		team = self.app_subscription_pg.team
		with SessionManager(team) as _:
			currency, address = jingrow.db.get_value(
				"Team", team, ["currency", "billing_address"]
			)
			team_pg = jingrow.get_pg("Team", team)
			response = {
				"currency": currency,
				"address": jingrow.db.get_value(
					"Address",
					address,
					["address_line1", "city", "state", "country", "pincode"],
					as_dict=True,
				)
				if address
				else {},
				"team": self.app_subscription_pg.team,
				"countries": jingrow.db.get_all("Country", pluck="name"),
				"plans": get_site_plans(),
				"has_billing_info": (
					team_pg.default_payment_method
					or team_pg.get_balance() > 0
					or team_pg.free_account
				),
				"current_plan": jingrow.db.get_value("Site", self.app_subscription_pg.site, "plan"),
			}

			capture("attempted", "fc_subscribe", team)
			return response

	def update_billing_info(self, data: Dict) -> str:
		team = self.app_subscription_pg.team
		with SessionManager(team) as _:
			team_pg = jingrow.get_pg("Team", team)
			team_pg.update_billing_details(data)

			capture("updated_address", "fc_subscribe", team)
			return "success"

	def get_publishable_key_and_setup_intent(self):
		with SessionManager(self.app_subscription_pg.team) as _:
			from jcloud.api.billing import get_publishable_key_and_setup_intent

			return get_publishable_key_and_setup_intent()

	def setup_intent_success(self, setup_intent):
		team = self.app_subscription_pg.team
		with SessionManager(team) as _:
			from jcloud.api.billing import setup_intent_success

			capture("added_card", "fc_subscribe", team)
			return setup_intent_success(setup_intent)

	def change_site_plan(self, plan):
		team = self.app_subscription_pg.team
		with SessionManager(team) as _:
			site = jingrow.get_pg("Site", self.app_subscription_pg.site)
			site.change_plan(plan)
			capture("changed_plan", "fc_subscribe", team)

	def send_login_link(self):
		try:
			login_url = self.get_login_url()
			users = jingrow.get_pg("Team", self.app_subscription_pg.team).user
			jingrow.sendmail(
				subject="Login Verification Email",
				recipients=[users],
				template="remote_login",
				args={"login_url": login_url, "site": self.app_subscription_pg.site},
				now=True,
			)
			return "success"
		except Exception as e:
			return e

	def get_login_url(self):
		# check for active tokens
		team = self.app_subscription_pg.team
		if jingrow.db.exists(
			"Saas Remote Login",
			{
				"team": team,
				"status": "Attempted",
				"expires_on": (">", jingrow.utils.now()),
			},
		):
			pg = jingrow.get_pg(
				"Saas Remote Login",
				{
					"team": team,
					"status": "Attempted",
					"expires_on": (">", jingrow.utils.now()),
				},
			)
			token = pg.token
		else:
			token = jingrow.generate_hash("Saas Remote Login", 50)
			jingrow.get_pg(
				{
					"pagetype": "Saas Remote Login",
					"team": team,
					"token": token,
				}
			).insert(ignore_permissions=True)
			jingrow.db.commit()

		return get_url(
			f"/api/action/jcloud.api.marketplace.login_via_token?token={token}&team={team}&site={self.app_subscription_pg.site}"
		)


class SessionManager:
	# set user for authenticated requests and then switch to guest once completed
	def __init__(self, team: str):
		jingrow.set_user(jingrow.db.get_value("Team", team, "user"))

	def __enter__(self):
		return self

	def __exit__(self, exc_type, exc_value, exc_traceback):
		jingrow.set_user("Guest")


# ------------------------------------------------------------
# API ENDPOINTS
# ------------------------------------------------------------
@jingrow.whitelist(allow_guest=True)
def get_subscription_status(secret_key: str) -> str:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.get_subscription_status()


@jingrow.whitelist(allow_guest=True)
def get_subscription_info(secret_key: str) -> Dict:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.get_subscription_info()


@jingrow.whitelist(allow_guest=True)
def get_subscription(secret_key: str) -> str:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.get_subscription()


@jingrow.whitelist(allow_guest=True)
def get_plans(secret_key: str, subscription: str) -> List:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.get_plans(subscription)


@jingrow.whitelist(allow_guest=True)
def update_billing_info(secret_key: str, data) -> str:
	data = jingrow.parse_json(data)
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.update_billing_info(data)


@jingrow.whitelist(allow_guest=True)
def get_publishable_key_and_setup_intent(secret_key: str) -> str:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.get_publishable_key_and_setup_intent()


@jingrow.whitelist(allow_guest=True)
def setup_intent_success(secret_key: str, setup_intent) -> str:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.setup_intent_success(setup_intent)


@jingrow.whitelist(allow_guest=True)
def change_site_plan(secret_key: str, plan: str) -> str:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.change_site_plan(plan)


@jingrow.whitelist(allow_guest=True)
def send_login_link(secret_key: str) -> str:
	api_handler = DeveloperApiHandler(secret_key)
	return api_handler.send_login_link()
