import { OauthConnections, OauthConnectionTypes } from '@event-inc/types';
import {
	makeHttpNetworkCall,
	matchResultAndHandleHttpError,
} from '@event-inc/utils';
import { identity } from 'ramda';
import jwt_decode from 'jwt-decode';

interface OauthConnectionOptions {
	OAUTH_APP_CLIENT_ID: string;
	OAUTH_APP_CLIENT_SECRET: string;
	type: OauthConnections;
}

interface Tenants {
	id: string;
	tenantId: string;
	tenantType: string;
	tenantName: string;
	createdDateUtc: string;
	updatedDateUtc: string;
	authEventId: string;
}
[];

const endpoints = {
	xero: 'https://identity.xero.com/connect/token',
};

const deleteEndpoints = {
	xero: 'https://api.xero.com/connections/{connectionId}',
};

export class OauthConnection {
	private readonly OAUTH_APP_CLIENT_ID: string;
	private readonly OAUTH_APP_CLIENT_SECRET: string;
	private readonly type: string;

	constructor({
		OAUTH_APP_CLIENT_ID,
		OAUTH_APP_CLIENT_SECRET,
		type,
	}: OauthConnectionOptions) {
		this.OAUTH_APP_CLIENT_ID = OAUTH_APP_CLIENT_ID;
		this.OAUTH_APP_CLIENT_SECRET = OAUTH_APP_CLIENT_SECRET;
		this.type = type;
	}

	getOauthDetails = () => {
		switch (this.type) {
			case OauthConnectionTypes.Xero:
				return {
					endpoint: endpoints[this.type],
					headers: {
						'Content-Type': 'application/x-www-form-urlencoded',
						Authorization: `Basic ${Buffer.from(
							`${this.OAUTH_APP_CLIENT_ID}:${this.OAUTH_APP_CLIENT_SECRET}`
						).toString('base64')}`,
					},
					sourceIntegrationDefinitionId:
						'conn_def_src_MTY4ODc2MzM3ODY2OQ::OWE2NDdmYjAtZjFlMy00MWEyLTgwMTctMzVhODMxYTBkOWNi',
					destinationIntegrationDefinitionId:
						'conn_def_dst_MTY4NjA3MDUyOTk1OA::MTgwM2E1NGItMTYxMC00NjE4LTk1NTItNjBjY2I1NmY1ZmQx',
				};
		}
	};

	getOauthFormData = ({
		accessToken,
		refreshToken,
	}: {
		accessToken: string;
		refreshToken: string;
	}) => {
		switch (this.type) {
			case OauthConnectionTypes.Xero:
				return {
					XERO_REFRESH_TOKEN: refreshToken,
					XERO_ACCESS_TOKEN: accessToken,
					XERO_CLIENT_ID: this.OAUTH_APP_CLIENT_ID,
					XERO_CLIENT_SECRET: this.OAUTH_APP_CLIENT_SECRET,
				};
		}
	};

	deleteOauthConnection = async ({
		connectionId,
		accessToken,
	}: {
		connectionId: string;
		accessToken: string;
	}) => {
		const deleteEndpoint = deleteEndpoints[this.type as OauthConnections];

		const response = await makeHttpNetworkCall({
			method: 'DELETE',
			url: deleteEndpoint.replace('{connectionId}', connectionId),
			headers: {
				Authorization: `Bearer ${accessToken}`,
			},
		});

		return matchResultAndHandleHttpError(response, identity);
	};

	getTenantId = async (accessToken: string) => {
		switch (this.type) {
			case OauthConnectionTypes.Xero:
				const decodedToken: {
					authentication_event_id: string;
				} = jwt_decode(accessToken);
				const response = await makeHttpNetworkCall({
					method: 'GET',
					url: 'https://api.xero.com/connections',
					headers: {
						Authorization: `Bearer ${accessToken}`,
					},
					params: {
						authEventId: decodedToken?.authentication_event_id,
					},
				});

				let { data } = matchResultAndHandleHttpError(response, identity) as {
					data: Tenants[];
				};

				if (data?.length === 0 || data?.length > 1) {
					const existingTenants = await this.getAuthorizedTenants(accessToken);
					const tenantNames = existingTenants
						?.map((item) => item.tenantName)
						?.join(', ');
					const existingTenantsMessage =
						tenantNames?.length > 0
							? ` The following tenants are currently connected: ${tenantNames}.`
							: '';
					throw new Error(
						`No tenant was explicitly selected on connect.${existingTenantsMessage}Ensure you disconnect and connect again with a single tenant selected.`
					);
				}

				return data?.[0]?.id;
		}
	};

	getAuthorizedTenants = async (accessToken: string) => {
		switch (this.type) {
			case OauthConnectionTypes.Xero:
				const response = await makeHttpNetworkCall({
					method: 'GET',
					url: 'https://api.xero.com/connections',
					headers: {
						Authorization: `Bearer ${accessToken}`,
					},
				});

				const { data } = matchResultAndHandleHttpError(response, identity) as {
					data: Tenants[];
				};
				return data;
		}
	};
}

