import { useHTTP } from "hooks";
// import { useWebsocket } from "websocket";
import { pond } from "protobuf-ts/pond";
import { createContext, PropsWithChildren, useContext } from "react";
import { dateRange } from "providers/http";
import { getComponentIDString } from "pbHelpers/Component";
import { Component } from "models";
import { pondURL } from "./pond";
import { AxiosResponse } from "axios";
import { useGlobalState } from "providers";
import { quack } from "protobuf-ts/quack";

export interface IComponentAPIContext {
  newCron: (
    device: number,
    componentId: string,
    time: string,
    url: string,
    componentKey: string
  ) => Promise<any>;
  add: (device: number, settings: pond.ComponentSettings, otherTeam?: string) => Promise<AxiosResponse<pond.AddComponentResponse>>;
  addMultiComponents: (
    device: number,
    components: pond.MultiComponentSettings,
     otherTeam?: string
  ) => Promise<AxiosResponse<pond.AddMultiComponentsResponse>>;
  update: (
    device: number,
    settings: pond.ComponentSettings,
    keys?: string[],
    types?: string[],
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.UpdateComponentResponse>>;
  remove: (device: number, component: string, keys?: string[], types?: string[], otherTeam?: string) => Promise<AxiosResponse<pond.RemoveComponentResponse>>;
  get: (device: number, component: string, keys?: string[], types?: string[], otherTeam?: string) => Promise<AxiosResponse<pond.Component>>;
  list: (
    device: number | string,
    demo?: boolean,
    keys?: string[],
    types?: string[],
    comprehensive?: boolean,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.ListComponentsResponse>>;
  listForObject: (
    keys: string[],
    types: string[],
    otherTeam?:string
  ) => Promise<AxiosResponse<pond.ListComponentsResponse>>;
  listComponentCardData: (
    device: number | string,
    demo?: boolean,
    keys?: string[],
    types?: string[],
    comprehensive?: boolean,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.ListComponentCardDataResponse>>;
  listHistory: (
    deviceId: string | number,
    componentKey: string,
    limit: number,
    offset: number,
    keys?: string[],
    types?: string[]
  ) => Promise<AxiosResponse<pond.ListComponentHistoryResponse>>;
  // Old measuremnt structure functions, no longer used in codebase
  // listMeasurements: (
  //   device: number,
  //   component: string,
  //   startDate: any,
  //   endDate: any,
  //   limit: number,
  //   offset: number,
  //   order: string,
  //   orderBy: string,
  //   demo?: boolean,
  //   keys?: string[],
  //   types?: string[]
  // ) => //exportMeasurements?: boolean
  // Promise<AxiosResponse<pond.ListMeasurementsResponse>>;
  // sampleMeasurements: (
  //   device: number | string,
  //   component: string,
  //   startDate: any,
  //   endDate: any,
  //   sampleSize: number,
  //   demo?: boolean,
  //   keys?: string[],
  //   types?: string[],
  //   otherTeam?: string
  // ) => Promise<AxiosResponse<pond.SampleMeasurementsResponse>>;
  sampleUnitMeasurements: (
    device: number | string,
    component: string,
    startDate: any,
    endDate: any,
    sampleSize: number,
    demo?: boolean,
    keys?: string[],
    types?: string[],
    showErrors?: boolean,
    allNodes?: boolean,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.SampleUnitMeasurementsResponse>>;
  // possibly a deprecated function as it is not used anywhere
  updateComponentPreferences: (
    id: number | string,
    component: string,
    preferences: pond.UserPreferences,
    keys?: string[],
    types?: string[]
  ) => Promise<any>;
  listUnitMeasurements: (
    device: number,
    component: string,
    startDate: any,
    endDate: any,
    limit: number,
    offset: number,
    order: string,
    measurementType?: number | quack.MeasurementType,
    keys?: string[],
    types?: string[],
    showErrors?: boolean,
    allNodes?: boolean,
    otherTeam?: string
  ) => Promise<AxiosResponse<pond.ListUnitMeasurementsResponse>>;
}

export const ComponentAPIContext = createContext<IComponentAPIContext>({} as IComponentAPIContext);

interface Props {}

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

  const newCron = (
    device: number,
    componentId: string,
    time: string,
    url: string,
    componentKey: string
  ) => {
    return post(
      pondURL(
        "/cron/components/arcgis?device=" +
          device +
          "&componentId=" +
          componentId +
          "&componentKey=" +
          componentKey +
          "&time=" +
          time +
          "&urlEndpoint=" +
          url
      )
    );
  };

  const addComponent = (device: number, settings: pond.ComponentSettings, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    let url = "/devices/" + device + "/components"
    if (view) url = url + `?as=${view}`
    return new Promise<AxiosResponse<pond.AddComponentResponse>>((resolve, reject) => {
      post<pond.AddComponentResponse>(pondURL(url), settings).then(resp => {
        resp.data = pond.AddComponentResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const addMultiComponents = (device: number, components: pond.MultiComponentSettings, otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    let url = "/devices/" + device + "/multiComponents"
    if (view) url = url + `?as=${view}`
    return new Promise<AxiosResponse<pond.AddMultiComponentsResponse>>((resolve, reject)=>{
      post<pond.AddMultiComponentsResponse>(pondURL(url), components).then(resp => {
        resp.data = pond.AddMultiComponentsResponse.fromObject(resp.data)
        resolve(resp)
      }).catch(err => {
        reject(err)
      })
    })
  };

  const updateComponent = (
    device: number,
    settings: pond.ComponentSettings,
    keys?: string[],
    types?: string[],
    otherTeam?: string
  ) => {
    let k: string[] = keys ? keys : [];
    if (!k.includes(device.toString())) k.push(device.toString()); //if the device id is already in the keys do not add it again
    let t: string[] = types ? types : [];
    if (!t.includes("device")) t.push("device"); // if "device" is already in the types do not add it again
    const view = otherTeam ? otherTeam : as
    const url = pondURL(
      "/devices/" +
        device +
        "/components/" +
        getComponentIDString(Component.any({ settings })) +
        "/update" +
        ("?keys=" + k) +
        ("&types=" + t) +
        (view ? "&as=" + view : "")
    );
    return new Promise<AxiosResponse<pond.UpdateComponentResponse>>((resolve, reject)=>{
      put<pond.UpdateComponentResponse>(url, settings).then(resp => {
        resp.data = pond.UpdateComponentResponse.fromObject(resp.data)
        resolve(resp)
      }).catch(err => {
        reject(err)
      })
    })
  };

  const removeComponent = (
    device: number,
    component: string,
    keys?: string[],
    types?: string[],
    otherTeam?: string
  ) => {
    let k: string[] = keys ? keys : [];
    if (!k.includes(device.toString())) k.push(device.toString()); //if the device id is already in the keys do not add it again
    let t: string[] = types ? types : [];
    if (!t.includes("device")) t.push("device"); // if "device" is already in the types do not add it again
    const view = otherTeam ? otherTeam : as
    const url = pondURL(
      "/devices/" +
        device +
        "/components/" +
        component +
        ("?keys=" + k) +
        ("&types=" + t) +
        (view ? "&as=" + view : "")
    );
    return new Promise<AxiosResponse<pond.RemoveComponentResponse>>((resolve, reject) => {
      del<pond.RemoveComponentResponse>(url).then(resp => {
        resp.data = pond.RemoveComponentResponse.fromObject(resp)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const getComponent = (device: number, component: string, keys?: string[], types?: string[], otherTeam?: string) => {
    let k: string[] = keys ? keys : [];
    if (!k.includes(device.toString())) k.push(device.toString()); //if the device id is already in the keys do not add it again
    let t: string[] = types ? types : [];
    if (!t.includes("device")) t.push("device"); // if "device" is already in the types do not add it again
    const view = otherTeam ? otherTeam : as
    const url = pondURL(
      "/devices/" +
        device +
        "/components/" +
        component +
        ("?keys=" + k) +
        ("&types=" + t) +
        (view ? "&as=" + view : "")
    );
    return new Promise<AxiosResponse<pond.Component>>((resolve, reject) => {
      get<pond.Component>(url).then(resp => {
        resp.data = pond.Component.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const listComponents = (
    device: number | string,
    demo: boolean = false,
    keys?: string[],
    types?: string[],
    comprehensive?: boolean,
    otherTeam?: string
  ) => {
    let k = keys ? keys : [];
    let t = types ? types : [];
    const view = otherTeam ? otherTeam : as
    let url = pondURL(
      "/devices/" +
        device +
        "/components?keys=" +
        k +
        "&types=" +
        t +
        (comprehensive ? "&comprehensive=" + comprehensive.toString() : ""),
      demo
    );
    if (view)
      url = pondURL(
        `/devices/${device}/components?as=${view}&keys=${k}&types=${t}` +
          (comprehensive ? "&comprehensive=" + comprehensive.toString() : ""),
        demo
      );
    return new Promise<AxiosResponse<pond.ListComponentsResponse>>((resolve, reject) => {
      get<pond.ListComponentsResponse>(url).then(resp => {
        resp.data = pond.ListComponentsResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const listComponentsForObject = (keys: string[], types: string[], otherTeam?: string) => {
    const view = otherTeam ? otherTeam : as
    let url = pondURL("/components/forObject?keys=" + keys + "&types=" + types + (view ? "&as=" + view : ""))
    return new Promise<AxiosResponse<pond.ListComponentsResponse>>((resolve, reject) => {
      get<pond.ListComponentsResponse>(url).then(resp => {
        resp.data = pond.ListComponentsResponse.fromObject(resp.data)
        resolve(resp)
      }).catch(err => {
        reject(err)
      })
    })
  };

  const listComponentCardData = (
    device: number | string,
    demo: boolean = false,
    keys?: string[],
    types?: string[],
    comprehensive = false,
    otherTeam?: string
  ) => {
    const view = otherTeam ? otherTeam : as
    let url = pondURL(
      "/devices/" +
        device +
        "/componentCards" +
        "?comprehensive=" +
        comprehensive +
        (keys ? "&keys=" + keys : "&keys=" + [device.toString()]) +
        (types ? "&types=" + types : "&types=" + ["device"]),

      demo
    );
    if (view)
      url = pondURL(
        `/devices/${device}/componentCards?as=${view}` +
          (keys ? "&keys=" + keys : "&keys=" + [device.toString()]) +
          (types ? "&types=" + types : "&types=" + ["device"]),
        demo
      );
    return new Promise<AxiosResponse<pond.ListComponentCardDataResponse>>((resolve,reject)=>{
      get<pond.ListComponentCardDataResponse>(url).then(resp => {
        resp.data = pond.ListComponentCardDataResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const listHistory = (
    deviceId: string | number,
    componentKey: string,
    limit: number,
    offset: number,
    keys?: string[],
    types?: string[]
  ) => {
    let url = pondURL(
      "/devices/" +
        deviceId +
        "/components/" +
        componentKey +
        "/history?limit=" +
        limit +
        "&offset=" +
        offset +
        (keys ? "&keys=" + keys : "&keys=" + [deviceId.toString()]) +
        (types ? "&types=" + types : "&types=" + ["device"])
    )
    return new Promise<AxiosResponse<pond.ListComponentHistoryResponse>>((resolve,reject) => {
      get<pond.ListComponentHistoryResponse>(url).then(resp => {
        resp.data = pond.ListComponentHistoryResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  // const listMeasurements = (
  //   device: number,
  //   component: string,
  //   startDate: any,
  //   endDate: any,
  //   limit: number,
  //   offset: number,
  //   order: string,
  //   orderBy: string,
  //   demo: boolean = false,
  //   keys?: string[],
  //   types?: string[],
  //   otherTeam?: string
  //   // exportMeasurements: boolean = false
  // ) => {
  //   const url = pondURL(
  //     "/devices/" +
  //       device +
  //       "/components/" +
  //       component +
  //       "/measurements" +
  //       //(exportMeasurements ? "/export" : "") +
  //       dateRange(startDate, endDate) +
  //       "&limit=" +
  //       limit +
  //       "&offset=" +
  //       offset +
  //       "&order=" +
  //       order +
  //       "&by=" +
  //       orderBy +
  //       (as ? "&as=" + as : "") +
  //       (keys ? "&keys=" + keys : "&keys=" + [device]) +
  //       (types ? "&types=" + types : "&types=" + ["device"]),
  //     demo
  //   );
  //   return new Promise<AxiosResponse<pond.ListMeasurementsResponse>>((resolve,reject) => {
  //     return get<pond.ListMeasurementsResponse>(url).then(resp => {
  //       resp.data = pond.ListMeasurementsResponse.fromObject(resp.data)
  //       return resolve(resp)
  //     }).catch(err => {
  //       return reject(err)
  //     })
  //   })
  // };

  // const sampleMeasurements = (
  //   device: number | string,
  //   component: string,
  //   startDate: any,
  //   endDate: any,
  //   sampleSize: number,
  //   demo: boolean = false,
  //   keys?: string[],
  //   types?: string[],
  //   otherTeam?: string
  // ) => {
  //   const url = pondURL(
  //     "/devices/" +
  //       device +
  //       "/components/" +
  //       component +
  //       "/measurements/sample" +
  //       dateRange(startDate, endDate) +
  //       "&size=" +
  //       sampleSize +
  //       (as ? "&as=" + as : "") +
  //       (keys ? "&keys=" + keys : "&keys=" + [device]) +
  //       (types ? "&types=" + types : "&types=" + ["device"]),
  //     demo
  //   );
  //   return new Promise<AxiosResponse<pond.SampleMeasurementsResponse>>((resolve, reject) => {
  //     get<pond.SampleMeasurementsResponse>(url).then(resp => {
  //       resp.data = pond.SampleMeasurementsResponse.fromObject(resp.data)
  //       return resolve(resp)
  //     }).catch(err => {
  //       return reject(err)
  //     })
  //   })
  // };

  const sampleUnitMeasurements = (
    device: number | string,
    component: string,
    startDate: any,
    endDate: any,
    sampleSize: number,
    demo: boolean = false,
    keys?: string[],
    types?: string[],
    showErrors: boolean = true,
    allNodes: boolean = false,
    otherTeam?: string
  ) => {
    const view = otherTeam ? otherTeam : as
    const url = pondURL(
      "/devices/" +
        device +
        "/components/" +
        component +
        "/measurements/sampleUnit" +
        dateRange(startDate, endDate) +
        "&size=" +
        sampleSize +
        (view ? "&as=" + view : "") +
        (keys ? "&keys=" + keys : "&keys=" + [device]) +
        (types ? "&types=" + types : "&types=" + ["device"]) +
        (showErrors ? "&showErrors=true" : "&showErrors=false")+
        (allNodes ? "&allNodes=true" : "&allNodes=false"),
      demo
    );
    return new Promise<AxiosResponse<pond.SampleUnitMeasurementsResponse>>((resolve, reject) => {
      get<pond.SampleUnitMeasurementsResponse>(url).then(resp => {
        resp.data = pond.SampleUnitMeasurementsResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const updateComponentPreferences = (
    id: number | string,
    component: string,
    preferences: pond.UserPreferences,
    keys?: string[],
    types?: string[]
  ) => {
    const url = pondURL(
      "/devices/" +
        id +
        "/components/" +
        component +
        "/preferences" +
        (keys ? "?keys=" + keys : "?keys=" + [id.toString()]) +
        (types ? "&types=" + types : "&types=" + ["device"])
    );
    return new Promise<AxiosResponse>((resolve, reject) => {
      put(url, preferences).then(resp => {
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  const listUnitMeasurements = (
    device: number,
    component: string,
    startDate: any,
    endDate: any,
    limit: number,
    offset: number,
    order: string,
    measurementType?: number | quack.MeasurementType,
    keys?: string[],
    types?: string[],
    showErrors?: boolean,
    allNodes: boolean = false,
    otherTeam?: string
  ) => {
    const view = otherTeam ? otherTeam : as
    const url = pondURL(
      "/devices/" +
        device +
        "/components/" +
        component +
        "/unitMeasurements" +
        dateRange(startDate, endDate) +
        "&limit=" +
        limit +
        "&offset=" +
        offset +
        "&order=" +
        order +
        (measurementType ? "&type=" + measurementType : "") +
        (view ? "&as=" + view : "") +
        (keys ? "&keys=" + keys : "&keys=" + [device]) +
        (types ? "&types=" + types : "&types=" + ["device"]) +
        (showErrors ? "&showErrors=true" : "&showErrors=false") +
        (allNodes ? "&allNodes=true" : "&allNodes=false")
    );
    return new Promise<AxiosResponse<pond.ListUnitMeasurementsResponse>>((resolve,reject) => {
      get<pond.ListUnitMeasurementsResponse>(url).then(resp => {
        resp.data = pond.ListUnitMeasurementsResponse.fromObject(resp.data)
        return resolve(resp)
      }).catch(err => {
        return reject(err)
      })
    })
  };

  return (
    <ComponentAPIContext.Provider
      value={{
        newCron,
        add: addComponent,
        addMultiComponents,
        update: updateComponent,
        remove: removeComponent,
        get: getComponent,
        list: listComponents,
        listForObject: listComponentsForObject,
        listComponentCardData: listComponentCardData,
        listHistory,
        //listMeasurements: listMeasurements,
        //sampleMeasurements: sampleMeasurements,
        sampleUnitMeasurements,
        updateComponentPreferences: updateComponentPreferences,
        listUnitMeasurements
      }}>
      {children}
    </ComponentAPIContext.Provider>
  );
}

export const useComponentAPI = () => useContext(ComponentAPIContext);

// export const useComponentWebsocket = (
//   device: number | string,
//   component: string,
//   emitter: (m: any) => void,
//   rate: number = 0
// ) =>
//   useWebsocket(
//     "/devices/" + device + "/components/" + component,
//     m => Component.any(JSON.parse(m.data)),
//     emitter,
//     rate
//   );

// export const useComponentsWebsocket = (
//   device: number | string,
//   emitter: (c: Component) => void,
//   rate: number = 0
// ) => {
//   useWebsocket(
//     "/devices/" + device + "/components",
//     m => Component.any(JSON.parse(m.data)),
//     emitter,
//     rate
//   );
// };

// export const useMeasurementsWebsocket = (
//   device: number | string,
//   component: string,
//   emitter: (m: any) => void,
//   rate: number = 0,
//   keys?: string[],
//   types?: string[]
// ) => {
//   useWebsocket(
//     "/devices/" + device + "/components/" + component + "/measurements",
//     m => pond.Measurement.fromObject(JSON.parse(m.data)),
//     emitter,
//     rate,
//     keys,
//     types
//   );
// };
