# Copyright (c) 2023, JINGROW
# MIT License. See license.txt

from __future__ import annotations

import json

import jingrow
from jingrow import _
from google.auth.transport.requests import Request
from google.oauth2 import id_token
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import Flow
from googleapiclient.discovery import build
from oauthlib.oauth2 import AccessDeniedError

from jcloud.api.product_trial import _get_active_site as get_active_site_of_product_trial
from jcloud.utils import log_error


@jingrow.whitelist(allow_guest=True)
def login(product=None):
	flow = google_oauth_flow()
	authorization_url, state = flow.authorization_url()
	minutes = 5
	payload = {"state": state}
	if product:
		payload["product"] = product
	jingrow.cache().set_value(f"google_oauth_flow:{state}", payload, expires_in_sec=minutes * 60)
	return authorization_url


@jingrow.whitelist(allow_guest=True)
def callback(code=None, state=None):  # noqa: C901
	cached_key = f"google_oauth_flow:{state}"
	payload = jingrow.cache().get_value(cached_key)
	if not payload:
		return invalid_login()

	product = payload.get("product")
	product_trial = jingrow.db.get_value("Product Trial", product, ["name"], as_dict=1) if product else None

	def _redirect_to_login_on_failed_authentication():
		jingrow.local.response.type = "redirect"
		if product_trial:
			jingrow.local.response.location = f"/dashboard/saas/{product_trial.name}/login"
		else:
			jingrow.local.response.location = "/dashboard/login"

	try:
		flow = google_oauth_flow()
		flow.fetch_token(authorization_response=jingrow.request.url)
	except AccessDeniedError:
		_redirect_to_login_on_failed_authentication()
		return None
	except Exception as e:
		log_error("Google Login failed", data=e)
		_redirect_to_login_on_failed_authentication()
		return None

	# authenticated
	jingrow.cache().delete_value(cached_key)

	# id_info
	token_request = Request()
	google_credentials = get_google_credentials()
	id_info = id_token.verify_oauth2_token(
		id_token=flow.credentials._id_token,
		request=token_request,
		audience=google_credentials["web"]["client_id"],
	)

	email = id_info.get("email")

	# phone (this may return nothing if info doesn't exists)
	phone_number = ""
	if flow.credentials.refresh_token:  # returns only for the first authorization
		credentials = Credentials.from_authorized_user_info(json.loads(flow.credentials.to_json()))
		service = build("people", "v1", credentials=credentials)
		person = service.people().get(resourceName="people/me", personFields="phoneNumbers").execute()
		if person and person.get("phoneNumbers"):
			phone_number = person.get("phoneNumbers")[0].get("value")

	team_name, team_enabled = jingrow.db.get_value("Team", {"user": email}, ["name", "enabled"]) or [0, 0]

	if team_name and not team_enabled:
		jingrow.throw(_("Account {0} has been deactivated").format(email))
		return None

	# if team exitst and  oauth is not using in saas login/signup flow
	if team_name and not product_trial:
		# login to existing account
		jingrow.local.login_manager.login_as(email)
		jingrow.local.response.type = "redirect"
		jingrow.local.response.location = "/dashboard"
		return None

	# create account request
	account_request = jingrow.get_pg(
		pagetype="Account Request",
		email=email,
		first_name=id_info.get("given_name"),
		last_name=id_info.get("family_name"),
		phone_number=phone_number,
		role="Jcloud Admin",
		oauth_signup=True,
		product_trial=product_trial.name if product_trial else None,
	)
	account_request.insert(ignore_permissions=True)
	jingrow.db.commit()

	if team_name and product_trial:
		jingrow.local.login_manager.login_as(email)
		active_site = get_active_site_of_product_trial(product_trial.name, team_name)
		jingrow.local.response.type = "redirect"
		if active_site:
			product_trial_request = jingrow.get_value(
				"Product Trial Request", {"site": active_site, "product_trial": product}, ["name"], as_dict=1
			)
			jingrow.local.response.location = f"/dashboard/saas/{product_trial.name}/login-to-site?product_trial_request={product_trial_request.name}"
		else:
			jingrow.local.response.location = (
				f"/dashboard/saas/{product_trial.name}/setup?account_request={account_request.name}"
			)
	else:
		# create/setup account
		jingrow.local.response.type = "redirect"
		jingrow.local.response.location = account_request.get_verification_url()
	return None


def invalid_login():
	jingrow.local.response["http_status_code"] = 401
	return "Invalid state parameter. The session timed out. Please try again or contact  Jingrow support at https://jingrow.com/support"


def google_oauth_flow():
	google_credentials = get_google_credentials()
	redirect_uri = google_credentials["web"].get("redirect_uris")[0]
	redirect_uri = redirect_uri.replace("jcloud.api.oauth.callback", "jcloud.api.google.callback")
	return Flow.from_client_config(
		client_config=google_credentials,
		scopes=[
			"https://www.googleapis.com/auth/userinfo.profile",
			"openid",
			"https://www.googleapis.com/auth/userinfo.email",
		],
		redirect_uri=redirect_uri,
	)


def get_google_credentials():
	if jingrow.local.dev_server:
		import os

		# flow.fetch_token doesn't work with http, so this is needed for local development
		os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

	config = jingrow.conf.get("google_credentials")
	if not config:
		jingrow.throw("google_credentials not found in site_config.json")
	return config
