import { AxiosInstance } from 'axios'
import queryString from 'query-string'

import {
	BlockchainFunction,
	BlockchainFunctionParams,
	TransferTicketsRequest,
} from '../blockchain/types'
import { buildQuery } from '../common/query'
import {
	BlockchainTicket,
	CreateTicketHolder,
	GetTicket,
	GetTickets,
	QRCode,
	TicketHolderId,
	TransferParams,
	UpdateTicketHolder,
} from './types'
import { Ticket, TicketHolder } from '../event/types'
import { HealthStatus, IdParam } from '../common/types'

/**
 * Service class for ticket API calls. Requires an organizer to be set (call to `useOrganizer`)
 */
export class TicketService {
	constructor(readonly client: AxiosInstance, readonly version: string) { }

	/**
	 * Returns true if the service is reachable
	 * Currently the
	 *
	 * @returns Services' online status
	 */
	async health(): Promise<HealthStatus> {
		try {
			const res = await this.client.get(`event/health`)
			if (res.data.status === 'ok') {
				return { online: true }
			}
		} catch (e) {
			// Do nothing
		}

		return { online: false }
	}

	/**
	 * Returns a ticket by its ID
	 *
	 * @param req ID of the order
	 * @returns `Ticket`
	 */
	async getTicket(req: GetTicket): Promise<Ticket> {
		const query = queryString.stringify(
			{
				with: !req.with ? undefined : buildQuery(req.with),
			},
			{ arrayFormat: 'comma', skipNull: true, skipEmptyString: true }
		)
		const res = await this.client.get(
			`event/${this.version}/ticket/${req.id}?${query}`
		)

		return res.data.data
	}

	/**
	 * Returns a list of tickets accessible to the user
	 *
	 * @param req Filters
	 * @returns `Ticket[]` Tickets which belong to the user
	 */
	async getTickets(req: GetTickets): Promise<Ticket[]> {
		const query = queryString.stringify(
			{
				with: !req.with ? undefined : buildQuery(req.with),
				with_enrollment_id: req.withEnrollmentId,
				format: req.format,
				status: req.status,
			},
			{ arrayFormat: 'comma', skipNull: true, skipEmptyString: true }
		)
		const res = await this.client.get(`event/${this.version}/ticket?${query}`)

		return res.data.data
	}

	/**
	 * Get ticket holder information together with the questionnaire attached to it
	 *
	 * @param ticketId 
	 * @param ticketHolderId 
	 */
	async getTicketHolder(
		ticketHolderId: TicketHolderId
	): Promise<TicketHolder> {
		const res = await this.client.get(
			`event/${this.version}/ticket/${ticketHolderId.ticketId}/ticket_holder/${ticketHolderId.id}?with=questionnaire.questionnaire_item.question`
		)

		return res.data.data
	}

	/**
	 * Creates a ticket holder for the specified ticket
	 *
	 * @param ticketId Id of the ticket for which the ticket holder gets created
	 * @param holder Ticket holder information
	 * @returns TicketHolder
	 */
	async createTicketHolder(
		ticketId: IdParam,
		holder: CreateTicketHolder
	): Promise<TicketHolder> {
		const res = await this.client.post(
			`event/${this.version}/ticket/${ticketId.id}/ticket_holder`,
			holder
		)

		return res.data.data
	}

	/**
	 * Creates a ticket holder for the specified ticket
	 *
	 * @param holder Ticket holder information
	 * @returns TicketHolder
	 */
	async updateTicketHolder(
		id: TicketHolderId,
		holder: UpdateTicketHolder
	): Promise<TicketHolder> {
		const res = await this.client.patch(
			`event/${this.version}/ticket/${id.ticketId}/ticket_holder/${id.id}`,
			holder
		)

		return res.data.data
	}

	/**
	 * Returns a ticket transfer payload
	 *
	 * @param data Ticket ids and the new owner
	 * @returns Payload to use for `sendTransaction`
	 */
	async getTransferPayload(
		data: TransferParams
	): Promise<BlockchainFunctionParams<TransferTicketsRequest>> {
		const res = await this.client.post(
			`event/${this.version}/ticket/transfer`,
			data
		)

		return {
			fcn: BlockchainFunction.TransferTicket,
			args: res.data.data,
		}
	}

	/**
	 * Returns the base64 encoded QR-Code image for a given ticket
	 *
	 * @param ticketId ID of ticket
	 * @returns
	 */
	async getQrCode(ticketId: IdParam): Promise<QRCode> {
		const res = await this.client.post(
			`event/${this.version}/ticket/${ticketId.id}/qrcode`,
			{
				action: 'trx',
				fcn: 'ticket.invalidate',
			}
		)

		return res.data.data
	}

	/**
	 * Get ticket info based on the blockchain id
	 *
	 * @param blockchainTicket 
	 * @returns 
	 */
	async getTicketByBlockchainId(blockchainTicket: BlockchainTicket): Promise<Ticket> {
		const res = await this.client.get(
			`event/${this.version}/ticket/bc/${blockchainTicket.id}`
		)

		return res.data.data
	}
}
