import { AxiosResponse } from "axios";
import { useHTTP } from "hooks";
import { Scope, Team, User } from "models";
import { permissionToString } from "pbHelpers/Permission";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import { createContext, PropsWithChildren, useContext } from "react";
import { objectQueryParams, pondURL } from "./pond";

export interface IPermissionAPIContext {
  getPermissions: (user: string, scope: Scope) => Promise<AxiosResponse<any>>;
  removePermissions: (user: string, scope: Scope) => Promise<AxiosResponse<any>>;
  updatePermissions: (scope: Scope, users: User[] | Team[]) => Promise<AxiosResponse<any>>;
  updateRelativePermissions: (
    parentScope: Scope,
    childScope: Scope,
    permissions: pond.Permission[]
  ) => Promise<AxiosResponse<any>>;
  shareObject: (
    scope: Scope,
    email: string,
    permissions: pond.Permission[],
    useImitation?: boolean
  ) => Promise<AxiosResponse<any>>;
  shareObjectByKey: (
    scope: Scope,
    key: string,
    permissions: pond.Permission[]
  ) => Promise<AxiosResponse<any>>;
  addShareableLink: (scope: Scope, expiration: string) => Promise<AxiosResponse<any>>;
  removeShareableLink: (scope: Scope, code: string) => Promise<AxiosResponse<any>>;
  listShareableLinks: (scope: Scope) => Promise<AxiosResponse<any>>;
  getShareableLink: (scope: Scope, code: string) => Promise<AxiosResponse<any>>;
}

export const PermissionAPIContext = createContext<IPermissionAPIContext>(
  {} as IPermissionAPIContext
);

interface Props {}

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

  const getPermissions = (user: string, scope: Scope) => {
    return new Promise<AxiosResponse>((resolve, reject) => {
      get(pondURL("/users/" + user + "/permissions" + objectQueryParams(scope))).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const removePermissions = (user: string, scope: Scope) => {
    return new Promise<AxiosResponse>((resolve, reject) => {
      del(pondURL("/users/" + user + "/permissions" + objectQueryParams(scope))).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const updatePermissions = (scope: Scope, users: User[] | Team[]) => {
    interface user {
      id: string;
      permissions: string[];
    }
    interface request {
      users: user[];
    }
    let body: request = { users: [] };
    users.forEach((user: User | Team) => {
      body.users.push({
        id: user.id(),
        permissions: user.permissions.map(permission => permissionToString(permission))
      });
    });
    return new Promise<AxiosResponse>((resolve, reject) => {
      put(pondURL("/" + scope.kind + "s/" + scope.key + "/permissions"), body).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const shareObject = (
    scope: Scope,
    email: string,
    permissions: pond.Permission[],
    useImitation = true
  ) => {
    let sql = "/" + scope.kind + "s/" + scope.key + "/share";
    if (as && useImitation) sql = sql + "?as=" + as;
    return new Promise<AxiosResponse>((resolve, reject) => {
      post(pondURL(sql), {
        email: email,
        permissions: permissions.map(permission => permissionToString(permission))
      }).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const updateRelativePermissions = (
    parentScope: Scope,
    childScope: Scope,
    permissions: pond.Permission[]
  ) => {
    let sql = "/updateRelativePermissions";
    if (as) sql = sql + "?as=" + as;
    return new Promise<AxiosResponse>((resolve, reject) => {

      put(pondURL(sql), {
        parent: parentScope.key,
        parentType: parentScope.kind,
        child: childScope.key,
        childType: childScope.kind,
        permissions: permissions.map(permission => permissionToString(permission))
      }).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const shareObjectByKey = (scope: Scope, key: string, permissions: pond.Permission[]) => {
    let url = pondURL("/" + scope.kind + "s/" + scope.key + "/shareByKey")
    if (as) url = pondURL(`/${scope.kind}s/${scope.key}/shareByKey?as=${as}`)
    return new Promise<AxiosResponse>((resolve, reject) => {

      post(url, {
        key: key,
        permissions: permissions.map(permission => permissionToString(permission))
      }).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const addShareableLink = (scope: Scope, expiration: string) => {
    let shareBody: pond.ShareableLinkSettings = pond.ShareableLinkSettings.create({
      expiration: expiration
    });
    return new Promise<AxiosResponse>((resolve, reject) => {
      return post(pondURL("/" + scope.kind + "s/" + scope.key + "/links"), shareBody).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const removeShareableLink = (scope: Scope, code: string) => {
    return new Promise<AxiosResponse>((resolve, reject) => {
      del(pondURL("/" + scope.kind + "s/" + scope.key + "/links/" + code)).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const listShareableLinks = (scope: Scope) => {
    return new Promise<AxiosResponse>((resolve, reject) => {
      get(pondURL("/" + scope.kind + "s/" + scope.key + "/links")).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const getShareableLink = (scope: Scope, code: string) => {
    return new Promise<AxiosResponse>((resolve, reject) => {
      get(pondURL("/" + scope.kind + "s/" + scope.key + "/links/" + code)).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
    
  };

  return (
    <PermissionAPIContext.Provider
      value={{
        getPermissions,
        removePermissions,
        updatePermissions,
        updateRelativePermissions,
        shareObject,
        shareObjectByKey,
        addShareableLink,
        removeShareableLink,
        listShareableLinks,
        getShareableLink
      }}>
      {children}
    </PermissionAPIContext.Provider>
  );
}

export const usePermissionAPI = () => useContext(PermissionAPIContext);
