import { Jwt } from '../auth/types'
import { AuthService } from '../auth'
import { RefreshableJwt } from '..'
import { ICredential, CredentialType } from './credential'
import { extractOrganizersFromJwt } from '../utils/jwt'

/**
 * Allows for the impersonation scenario to have automatic refreshes of the token.
 * The refreshes are done with the impersonatorToken since we cannot refresh an impersonated token
 */
export class ImpersonateCredentials implements ICredential {
	readonly type: CredentialType.Impersonate

	private impersonatedJwt: Jwt
	private impersonatedOrganizer?: string

	/** Uses the impersonatorJwt to obtain an impersonation token for the impersonatedId */
	constructor(
		private impersonatorJwt: Jwt | RefreshableJwt,
		private impersonatedId: number
	) {}

	getToken(): Jwt | RefreshableJwt {
		return this.impersonatedJwt
	}

	getImpersonatorToken(): Jwt | RefreshableJwt {
		return this.impersonatorJwt
	}

	async authorize(
		authService: AuthService,
		organizer?: string
	): Promise<Jwt | RefreshableJwt> {
		if (organizer) {
			this.impersonatedOrganizer = organizer
		}

		// If the organizer was requested, the jwt already exists and
		// if the token does not contain the permissions for the required organizer
		if (
			!this.hasOrganizerInToken(this.impersonatedJwt, organizer) // Fallback until we remove returning all permissions by default
		) {
			this.impersonatedJwt = await authService.impersonate(
				{
					userId: this.impersonatedId,
					organizerId: this.impersonatedOrganizer,
				},
				this.impersonatorJwt
			)
		}
		return this.impersonatedJwt
	}

	async refreshToken(
		authService: AuthService,
		organizer?: string
	): Promise<Jwt | RefreshableJwt> {
		return this.authorize(authService, organizer)
	}

	private hasOrganizerInToken(jwt: Jwt, organizer: string) {
		if (!jwt?.token || !organizer) return false
		
		const orgsInToken = extractOrganizersFromJwt(jwt)
		return !!orgsInToken.find((org) => org == organizer)
	}
}
