// Response types
// ==============

import { user } from "./UserStore"

export type GenericResponse = {
	value: string,
	message: string,
	errors?: GenericResponse[],
	extra?: { [index: string]: Object },
}

export type User = {
	id: string,
	username: string,
	email: string,
	otp_enabled: boolean,
	subscription: Subscription,
	storage_space_used: number,
	filesystem_storage_used: number,
	filesystem_node_count: number,
	is_admin: boolean,
	balance_micro_eur: number,
	monthly_transfer_cap: number,
	monthly_transfer_used: number,
	file_viewer_branding: Map<string, string>,
	embed_domains: string,
	affiliate_user_name: string,
	checkout_country: string,
	checkout_name: string,
	checkout_provider: string,
}

export type Subscription = {
	id: string,
	name: string,
	type: string,
	file_size_limit: number,
	storage_limit: number,
	price_per_tb_storage: number,
	price_per_tb_bandwidth: number,
	monthly_transfer_cap: number,
	file_sharing: boolean,
}

// Utility funcs
// =============

export const get_endpoint = () => {
	if ((window as any).api_endpoint !== undefined) {
		return (window as any).api_endpoint as string
	}
	console.warn("api_endpoint property is not defined on window")
	return "/api"
}

export const get_hostname = () => {
	if ((window as any).server_hostname !== undefined) {
		return (window as any).server_hostname as string
	}
	console.warn("server_hostname property is not defined on window")
	return "undefined"
}

export const check_response = async (resp: Response) => {
	let text = await resp.text()
	if (resp.status >= 400) {
		let error: any
		try {
			error = JSON.parse(text) as GenericResponse
		} catch (err) {
			error = text
		}
		throw error
	}
	return JSON.parse(text)
}

export const dict_to_form = (dict: Object) => {
	let form = new FormData()
	for (let key of Object.keys(dict)) {
		if (dict[key] === undefined) {
			continue
		} else if (dict[key] instanceof Date) {
			form.append(key, new Date(dict[key]).toISOString())
		} else if (typeof dict[key] === "object") {
			form.append(key, JSON.stringify(dict[key]))
		} else {
			form.append(key, dict[key])
		}
	}
	return form
}

// API methods
// ===========

export type UserSession = {
	auth_key: string,
	creation_ip_address: string,
	user_agent: string,
	app_name: string,
	creation_time: string,
	last_used_time: string,
	valid_domains: string[],
}

// If cached_user is undefined it means that the value is not initialized yet,
// null means the user is not logged in
let cached_user: User = undefined

// The request which is currently in flight. Callers which arrive while the
// request is still running share this promise, so the user is only requested
// from the server once
let user_request: Promise<User> | null = null

export const get_user = async (): Promise<User> => {
	if (cached_user !== undefined) {
		return cached_user
	}
	if (user_request === null) {
		user_request = request_user()
	}

	const u = await user_request
	if (u === undefined) {
		// The request failed. Clear it so the next caller tries again
		user_request = null
	}

	return u
}

const request_user = async (): Promise<User> => {
	try {
		let resp = await fetch(get_endpoint() + "/user")
		if (resp.status === 401) {
			// User is not authenticated
			cached_user = null
		} else if (resp.status >= 400) {
			throw await resp.text()
		} else {
			cached_user = (await resp.json()) as User
		}
	} catch (err) {
		alert("failed to fetch user:\n" + err)
	}

	user.set(cached_user)
	return cached_user
}

export const put_user = async (data: Object) => {
	await check_response(await fetch(
		get_endpoint() + "/user",
		{ method: "PUT", body: dict_to_form(data) },
	))

	// Update the user store
	user.update((user: User) => {
		for (let key of Object.keys(data)) {
			user[key] = data[key]
		}
		return user
	})
}

export const logout_user = async (redirect_path: string) => {
	await check_response(await fetch(
		get_endpoint() + "/user/session",
		{ method: "DELETE" },
	))

	// document.cookie = "pd_auth_key=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
	window.location.pathname = redirect_path
}

export type VATRate = {
	name: string,
	vat: number,
	alpha2: string,
	alpha3: string,
}
export const get_misc_vat_rate = async (country_code: string) => {
	return await check_response(await fetch(get_endpoint() + "/misc/vat_rate/" + country_code)) as VATRate
}
