/**
 * This file is used to collect all functions that call to the 
 * PHP middleware of the SA team.
 */

import { getValidToken } from "../auth/auth";
import { ServiceRequest } from "../auth/phpPayload";
import { TargetData } from "../types/addInTypes";
import { PHPAddCardType } from "../types/phpMiddlewareTypes/phpInputTypes";
import { AddAttachmentsResponse, BoardsResponse, BsmapError, CheckAccessResponse, CreateCardResponse, CreateCommentResponse, GetBoardStructureResponse, isBsmapError, UsersResponse } from "../types/phpMiddlewareTypes/phpReturnTypes";
import { getMailData } from "./mailHander";

const glbBaseURL = "http://outlook.addin.localhost";

/**
 * Performs an HTTP request to the specified endpoint using the provided method and optional body.
 * 
 * Retrieves `subdomain` and `apikey` from localStorage and includes them in the request headers.
 * The request is sent as JSON and expects a JSON response.
 * 
 * @template T - The expected response type.
 * @param endpoint - The API endpoint to send the request to (relative to `glbBaseURL`).
 * @param method - The HTTP method to use for the request (e.g., 'GET', 'POST').
 * @param body - Optional request body to be sent as JSON.
 * @returns A promise that resolves to the parsed JSON response of type `T`.
 * @throws {Error} Throws an error if the fetch operation fails.
 */
const performFetch = async <T>(endpoint:string, method:'GET'|'POST', body?:any):Promise<Awaited<T | BsmapError>> => {
	const subdomain = localStorage.getItem('subdomain') || '';
	const apikey = localStorage.getItem('apikey') || '';
	
	const headers:Record<string,string> = {
		"Accept": "application/json",
		"Content-Type": "application/json",
		"subdomain": subdomain,
		"apikey": apikey
	};

	const options:RequestInit = { method: method, headers: headers };
	if (body) options.body = JSON.stringify(body);

	try {
		const response = await fetch(`${glbBaseURL}/${endpoint}`, options);
		const data = await response.json();
		return data;
	} catch (error:any) {
		throw new Error("Businessmap error 1: " + JSON.stringify(error.message));
	}
};

/**
 * Checks the authentication for BusinessMap using the provided subdomain and API key.
 *
 * Sends a POST request to the `checkAccess.php` endpoint to verify the credentials.
 * Optionally stores the subdomain and API key in localStorage if `setAsDefault` is true.
 *
 * @param subdomain - The subdomain to authenticate with.
 * @param apikey - The API key for authentication.
 * @param setAsDefault - If true, saves the subdomain and API key to localStorage. Defaults to false.
 * @returns A promise that resolves to `true` if authentication is successful, otherwise `false`.
 */
export const checkBsmapAuth = async (subdomain:string, apikey:string, setAsDefault:boolean = false) => {
	try {
		const response = await performFetch<CheckAccessResponse>(
			'checkAccess.php', 
			'POST', 
			{ subdomain: subdomain, apikey: apikey }
		);
		if (isBsmapError(response))
			throw new Error(`Error while checking the businessmap auth: ${JSON.stringify(response)}`);

		if (response.code != '200') return false; // TODO: Show message.

		if (setAsDefault) {
			localStorage.setItem('subdomain', subdomain);
			localStorage.setItem('apikey', response.data.apikey); // Encrypted api-key
		}

		return true;
	} catch (error:any) {
		console.error(error);
		return false;
	}
};

/**
 * Retrieves the structure of a board by its ID.
 *
 * Sends a GET request to the backend API to fetch the board structure.
 * Throws an error if the response indicates a business map error.
 *
 * @param board_id - The unique identifier of the board to retrieve the structure for.
 * @returns A promise that resolves to the board structure data.
 * @throws {Error} If there is an error while checking the business map authentication.
 */
export const getBoardStructure = async (board_id:number) => {
	const response = await performFetch<GetBoardStructureResponse>(`structure.php?board_id=${board_id}`, 'GET');
	if (isBsmapError(response))
		throw new Error(`Error while checking the businessmap auth: ${JSON.stringify(response)}`);
	
	return response.data;
};

/**
 * Adds attachments to a specified card by sending them to the backend API.
 *
 * @param cardId - The unique identifier of the card to which attachments will be added.
 * @param outlookAttachments - An array of Office.AttachmentDetails representing the attachments to be added.
 * @throws {Error} Throws an error if the response indicates an error.
 */
export const addAttachments = async (cardId:number, outlookAttachments:Office.AttachmentDetails[]) => {
	const serviceRequest = new ServiceRequest();
	serviceRequest.attachments = outlookAttachments;
	serviceRequest.card_id = cardId;
	serviceRequest.oauth2token = await getValidToken();
	// ... operator to turn the ServiceRequest class into an object.
	const response = await performFetch<AddAttachmentsResponse>(`attachments.php`, 'POST', {...serviceRequest});
	if (isBsmapError(response))
		throw new Error(`Error while adding the attachments: ${JSON.stringify(response)}`);

	return;
}

/**
 * Retrieves the owners (users) of a specified board by its ID.
 *
 * @param boardId - The unique identifier of the board whose owners are to be fetched.
 * @returns A promise that resolves to an array of users associated with the board.
 * @throws {Error} If there is an error while fetching the users of the specified board.
 */
export const getBoardOwners = async(boardId:number) => {
	const response = await performFetch<UsersResponse>(`users.php?board_id=${boardId}`, 'GET');
	if (isBsmapError(response))
		throw new Error(`Error while getting the users of a board id: ${JSON.stringify(response)}`);
	return response.data;
}

/**
 * Retrieves the list of boards from the API.
 *
 * @returns {Promise<BoardsResponse['data']>} A promise that resolves to the data containing the boards.
 * @throws {Error} Throws an error if the API response indicates a Bsmap error.
 */
export const getBoards = async(): Promise<{board_id: number; name: string;}[]> => {
	const response = await performFetch<BoardsResponse>('boards.php', 'GET');
	if (isBsmapError(response))
		throw new Error(`Error while getting the boards: ${JSON.stringify(response)}`);
	return response.data;
};

/**
 * Creates a new card by sending the provided data to the PHP backend API.
 *
 * This function gathers mail data, constructs the payload, and performs a POST request
 * to the 'add.php' endpoint. If the response indicates an error, it throws an exception.
 *
 * @param data - The target data containing card details such as column, lane, owner, and priority IDs.
 * @throws {Error} If the backend returns an error response.
 * @returns {Promise<number>} A promise that resolves into the card id when the card is successfully created.
 */
export const createCard = async(data:TargetData): Promise<number> => {
	const mailData = await getMailData();
	const payload:PHPAddCardType = {
		column_id: data.column_id,
		lane_id: data.lane_id,
		owner_user_id: data.owner_id,
		priority: data.priority_id,
		reporter_email: mailData.reporter_email,
		title: mailData.title,
		description: mailData.description
	};
	const response = await performFetch<CreateCardResponse>('add.php', 'POST', payload);
	if (isBsmapError(response))
		throw new Error(`Error while creating the card: ${JSON.stringify(response)}`);

	return response.data;
};

/**
 * Creates a comment on a specified card by sending mail data and attachments to the backend API.
 *
 * - Throws an error if the backend responds with an error.
 *
 * @param cardId - The unique identifier of the card to add the comment to.
 * @returns A promise that resolves with the created comments ID.
 * @throws {Error} If the backend API returns an error response.
 */
export const createCommentOnCard = async (cardId:number) => {
	const mailData = await getMailData();
	const mailAttachments = Office.context.mailbox.item.attachments;
	for (const attachment of mailAttachments){
		attachment.id = attachment.id.replace(/\//g, "-").replace(/\+/g, "_");
	}

	const createCommentPayload = {
		card_id: cardId,
		comment: mailData.description,
		mail_item_id: Office.context.mailbox.item.itemId.replace(/\//g, "-").replace(/\+/g, "_"),
		oauth2token: await getValidToken(),
		attachments: mailAttachments
	}

	const response = await performFetch<CreateCommentResponse>('comments.php', 'POST', createCommentPayload);
	if (isBsmapError(response))
		throw new Error(`Error while creating the comment: ${JSON.stringify(response)}`);

	return response.data;
};