import { useHTTP } from "hooks";
import { createContext, PropsWithChildren, useContext } from "react";
import { pond } from "protobuf-ts/pond";
import { pondURL } from "./pond";
import { AxiosResponse } from "axios";
import { useGlobalState } from "providers";
import { or } from "utils";

export interface ITransactionAPIContext {
  addTransaction: (
    transaction: pond.Transaction,
    fileIDs?: string[],
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.AddTransactionResponse>>;
  transactionPageData: (
    start: string,
    end: string,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.TransactionPageDataResponse>>;
  listTransactions: (
    start: string,
    end: string,
    fromObject?: pond.ObjectType,
    toObject?: pond.ObjectType,
    fromKey?: string,
    toKey?: string,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.ListTransactionsResponse>>;
  listTransactionsFor: (
    limit: number,
    offset: number,
    order: "asc" | "desc",
    objectKey: string,
    state?: pond.TransactionState,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.ListTransactionsForResponse>>
  revokeTransaction: (key: string, otherTeam?: string) => Promise<AxiosResponse<pond.RevokeTransactionResponse>>;
  updateTransaction: (
    key: string,
    data: pond.TransactionData,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.UpdateTransactionResponse>>;
  submitApproval: (key: string, approved: boolean, updatedData: pond.Transaction, otherTeam?: string) => Promise<AxiosResponse<pond.SubmitTransactionApprovalResponse>>;
  removeTransaction: (key: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveTransactionResponse>>
}

export const TransactionAPIContext = createContext<ITransactionAPIContext>(
  {} as ITransactionAPIContext
);

interface Props {}

export default function TransactionProvider(props: PropsWithChildren<Props>) {
  const { children } = props;
  const { get, post, put, del } = useHTTP();
  const [{ as }] = useGlobalState();

  const addTransaction = (transaction: pond.Transaction, fileIDs?: string[], otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    if (view)
      return post<pond.AddTransactionResponse>(
        pondURL(
          "/transactions?as=" + view + (fileIDs ? "&fileAttachments=" + fileIDs.toString() : "")
        ),
        transaction
      );
    return post<pond.AddTransactionResponse>(
      pondURL("/transactions" + (fileIDs ? "?fileAttachments=" + fileIDs.toString() : "")),
      transaction
    );
  };

  const listTransactions = (
    start: string,
    end: string,
    fromObject?: pond.ObjectType,
    toObject?: pond.ObjectType,
    fromKey?: string,
    toKey?: string,
    otherTeam?: string
  ) => {
    const view = otherTeam ? otherTeam : as
    if (view) {
      return get<pond.ListTransactionsResponse>(
        pondURL("/transactions?as=" + view + "&start=" + start + "&end=" + end)
      );
    }
    return get<pond.ListTransactionsResponse>(
      pondURL(
        "/transactions?start=" +
          start +
          "&end=" +
          end +
          (fromObject ? "&fromObject=" + fromObject : "") +
          (toObject ? "&toObject=" + toObject : "") +
          (fromKey ? "&fromKey=" + fromKey : "") +
          (toKey ? "&toKey=" + toKey : "")
      )
    );
  };

  const listTransactionsFor = (
    limit: number,
    offset: number,
    order: "asc" | "desc",
    objectKey: string,
    state?: pond.TransactionState,
    otherTeam?: string
  ) => {
    const view = otherTeam ? otherTeam : as
    const url = "/transactionsFor" +
    "?limit=" +
    limit +
    "&offset=" +
    offset +
    ("&order=" + or(order, "asc")) +
    "&object=" + objectKey +
    (state ? "&state=" + state : "") +
    (view ? "&as=" + view : "")

    return new Promise<AxiosResponse<pond.ListTransactionsForResponse>>((resolve, reject)=>{
      get<pond.ListTransactionsForResponse>(pondURL(url)).then(resp => {
        resp.data = pond.ListTransactionsForResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  }

  const updateTransaction = (key: string, data: pond.TransactionData, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    if (view)
      return post<pond.UpdateTransactionResponse>(
        pondURL("/transactions/" + key + "/update?as=" + view),
        data
      );
    return post<pond.UpdateTransactionResponse>(pondURL("/transactions/" + key + "/update"), data);
  };

  const transactionPageData = (start: string, end: string, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    if (view) {
      return get<pond.TransactionPageDataResponse>(
        pondURL("/transactionsPage?as=" + view + "&start=" + start + "&end=" + end)
      );
    }
    return get<pond.TransactionPageDataResponse>(
      pondURL("/transactionsPage?start=" + start + "&end=" + end)
    );
  };

  const revokeTransaction = (key: string, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    if (view) {
      return put<pond.RevokeTransactionResponse>(
        pondURL("/transactions/" + key + "/revoke?as=" + view)
      );
    }
    return put<pond.TransactionPageDataResponse>(pondURL("/transactions/" + key + "/revoke"));
  };

  const submitApproval = (key: string, approval: boolean, updatedData: pond.Transaction, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    let url = "/transactions/" + key + "/approval?approve=" + approval.toString()
    if(view){
      url = url + "&as=" + view
    }
    return post<pond.SubmitTransactionApprovalResponse>(pondURL(url), updatedData)
  }

  const removeTransaction = (key: string, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    let url = "/transactions/" + key + "/remove"
    if(view){
      url = url + "&as=" + view
    }
    return del<pond.RemoveTransactionResponse>(pondURL(url))
  }

  return (
    <TransactionAPIContext.Provider
      value={{
        addTransaction,
        listTransactions,
        listTransactionsFor,
        updateTransaction,
        transactionPageData,
        revokeTransaction,
        submitApproval,
        removeTransaction
      }}>
      {children}
    </TransactionAPIContext.Provider>
  );
}

export const useTransactionAPI = () => useContext(TransactionAPIContext);
