import { getCampaign, getCampaignObject } from "@sc/api/gql/campaigns";
import { client } from "@sc/api/gql/client";
import { allDomainsQuery } from "@sc/api/gql/domains";
import { AppConfig } from "@sc/modules/app";
import _ from "lodash";
import React, { Component } from "react";
import DomainSetupPopup from "../settings/DomainManagement/DomainSetup";
import CampaignFull, { JustCampaignFull } from "./AdvancedBuilder/CampaignFull";
import {
  CampaignHeader,
  PublishCampaignSuccess,
  style,
  TrackingScript,
} from "./shared";
import CampaignLegend from "./SimpleBuilder/CampaignLegend";
import { triggerAsyncHook } from "@sc/plugins";

const Popups = (props) => (
  <div>
    {props.popup === "domain" ? (
      <DomainSetupPopup
        {...props}
        step={
          props.match.params.step !== undefined
            ? props.match.params.step.toUpperCase()
            : false
        }
        campaignId={props.match.params.campaignId}
      />
    ) : null}

    {props.popup === "tracking" ? (
      <TrackingScript {...props} campaignId={props.match.params.campaignId} />
    ) : null}
  </div>
);

class Campaign extends Component {
  constructor(props) {
    super(props);
    this.state = {
      appDrawerVisible: false,
      content: [],
      showPublish: false,
      settings: {},
      count: 0,
      domainName: "",
      loading: true,
      error: false,
    };

    // canvas edit methods
    this.setActiveObject = this.setActiveObject.bind(this);
    this.addCanvasObject = this.addCanvasObject.bind(this);
    this.removeCanvasObject = this.removeCanvasObject.bind(this);
    this.duplicateCanvasObject = this.duplicateCanvasObject.bind(this);
    this.updateCanvasObject = this.updateCanvasObject.bind(this);
    this.handleCampaignUpdate = this.handleCampaignUpdate.bind(this);
    this.disconnectObjects = this.disconnectObjects.bind(this);
    this.connectObjects = this.connectObjects.bind(this);
    this.getCampaignState = this.getCampaignState.bind(this);
    this.setCampaignState = this.setCampaignState.bind(this);
    this.showPopup = this.showPopup.bind(this);
  }

  static contextType = AppConfig;

  async componentDidMount() {
    console.log("mounted");

    this.setState({ loading: true });
    const { match } = this.props;

    // 1. Get Domain Name for this Campaign
    const domainContent = await client.query({
      query: allDomainsQuery,
      variables: {
        userId: localStorage.userId,
      },
    });

    console.log({ domainContent });
    const domainNames = _.get(domainContent, "data.domains", []);

    const campaignDomainObject = domainNames.filter((itm) => {
      if (itm.campaign.length) {
        const matchingCampaign = itm.campaign.filter(
          (i) => i.id === match.params.campaignId
        );
        if (matchingCampaign.length) return itm.name;
      }
      return false;
    });

    console.log({ campaignDomainObject });

    const domainName = campaignDomainObject.length
      ? campaignDomainObject[0].name
      : false;

    console.log({ domainName });

    // 2. Get Campaign Objects
    const campaignContent = await client.query({
      query: getCampaign,
      variables: {
        id: match.params.campaignId,
      },
    });

    console.log({ campaignContent });

    if (
      !_.get(campaignContent, "data.campaign", false) &&
      !_.get(campaignContent, "loading", false)
    )
      window.location.href = "/login";

    const content = _.filter(
      _.get(campaignContent, "data.campaign.objects", []),
      (itm) => !_.get(itm, "deleted", false)
    );
    const error = _.get(campaignContent, "data.error", false);

    this.setState(
      {
        loading: false,
        error,
        content,
        count: content.length,
        domainName,
      },
      () => {
        console.log(this.state);
      }
    );
  }

  // Edit related methods
  addCanvasObject(obj) {
    console.log("testZYX", { obj });
    this.setState(
      (prevState) => {
        return {
          content: [...prevState.content, obj],
        };
      },
      () => {
        // persist the state change to the database
        this.handleCampaignUpdate(obj, "create");
      }
    );
  }

  async removeCanvasObject(id) {
    let obj = [];

    // const { getCampaignQuery } = this.props;

    this.setState(
      (prevState) => {
        // get the key of the id
        const key = prevState.content.findIndex((itm) => itm.id === id);

        obj = prevState.content[key];

        // re-render a new array without the item
        return {
          content: [
            ...prevState.content.slice(0, key),
            ...prevState.content.slice(key + 1),
          ],
        };
      },
      async () => {
        // persist the state change to the database
        await this.handleCampaignUpdate(
          {
            ...obj,
            deleted: true,
            oldCampaignId: this.props.getCampaignQuery.campaign.id,
            // connectToIds: []
          },
          "update"
        );

        // await getCampaignQuery.refetch({
        //   variables: { userId: localStorage.userId }
        // });
      }
    );
  }

  duplicateCanvasObject(id) {
    let obj = [];

    this.setState(
      (prevState) => {
        // get the key of the id
        const key = prevState.content.findIndex((itm) => itm.id === id);

        // create a duplicate version of the itm, but modify the id and the x, y position
        const duplicateItem = {
          ...prevState.content[key],
          // id: `node${Math.random().toString(36).slice(2)}`,
          id: "REPLACE_ME",
          x: prevState.content[key].x + 250,
          y: prevState.content[key].y + 25,
        };

        obj = duplicateItem;

        // generate a new array with the duplicated item
        return {
          content: [
            ...prevState.content.slice(0, key + 1),
            duplicateItem,
            ...prevState.content.slice(key + 1),
          ],
        };
      },
      () => {
        // persist the state change to the database
        this.handleCampaignUpdate(obj, "create");
      }
    );
  }

  updateCanvasObject(id, settings, dbUpdate = true) {
    this.setState(
      (prevState) => {
        // get the key of the id
        const key = prevState.content.findIndex((itm) => itm.id === id);

        return {
          content: [
            ...prevState.content.slice(0, key),
            settings,
            ...prevState.content.slice(key + 1),
          ],
        };
      },
      () => {
        // persist the state change to the database
        if (dbUpdate) this.handleCampaignUpdate(settings, "update");
      }
    );
  }

  connectObjects(id, destinationId, dbUpdate = true) {
    let updatedObject = {};

    console.log("connect object");
    this.setState(
      (prevState) => {
        // get the key of the id
        const content = prevState.content;

        const key = prevState.content.findIndex((itm) => itm.id === id);

        // now get the key of the line we want to disconnect
        const connectToObj = content.length
          ? content[key].connectTo.filter(
              (itm) => itm.hasOwnProperty("id") === true
            )
          : []; // filter out x, y connector

        const connectToKey = connectToObj.findIndex(
          (itm) => itm.id === destinationId
        );

        // now build a connectTo array with that key
        const connectTo = [
          ...connectToObj.slice(0, connectToKey),
          { id: destinationId, temporary: !dbUpdate },
          ...connectToObj.slice(connectToKey + 1),
        ];

        updatedObject = {
          ...prevState.content[key],
          connectTo,
        };

        return {
          content: [
            ...prevState.content.slice(0, key),
            updatedObject,
            ...prevState.content.slice(key + 1),
          ],
        };
      },
      () => {
        if (dbUpdate) this.handleCampaignUpdate(updatedObject, "update");
      }
    );
  }

  disconnectObjects(id, destinationId, dbUpdate = true) {
    let updatedObject = {};

    this.setState(
      (prevState) => {
        // get the key of the id
        const key = prevState.content.findIndex((itm) => itm.id === id);

        // now get the key of the line we want to disconnect
        const connectToObj = prevState.content[key].connectTo;
        const connectToKey = dbUpdate
          ? connectToObj.findIndex((itm) => itm.id === destinationId)
          : connectToObj.findIndex(
              (itm) => itm.hasOwnProperty("temporary") === true
            );

        // now build a connectTo array without that key
        const connectTo =
          connectToKey > -1
            ? [
                ...connectToObj.slice(0, connectToKey),
                ...connectToObj.slice(connectToKey + 1),
              ]
            : connectToObj.filter((itm) => itm.hasOwnProperty("id"));

        updatedObject = {
          ...prevState.content[key],
          connectTo,
        };

        return {
          content: [
            ...prevState.content.slice(0, key),
            updatedObject,
            ...prevState.content.slice(key + 1),
          ],
        };
      },
      () => {
        if (dbUpdate) this.handleCampaignUpdate(updatedObject, "update");
      }
    );
  }

  debounce = (func, wait, immediate) => {
    console.log(" debouncing ");
    var saveTimeout;
    return function() {
      var context = this,
        args = arguments;

      var callNow = immediate && !saveTimeout;
      clearTimeout(saveTimeout);

      saveTimeout = setTimeout(function() {
        saveTimeout = null;
        if (!immediate) {
          func.apply(context, args);
        }
      }, wait);

      if (callNow) func.apply(context, args);
    };
  };

  handleCampaignUpdate = async (object = false, action = false) => {
    // const settings = this.context;

    const {
      updateCampaignMutation,
      updateCampaignObjectMutation,
      createCampaignObjectMutation,
      getCampaignQuery,
      // updateObjectSettings,
      updatePageSettings,
    } = this.props;

    // const { content } = this.state;

    /**
     * Update the main campaign so that it is connected to all of the associated objects
     * (necessary for creating & deleting)
     */
    const updateCampaign = async (content) => {
      const objectsIds = content.map((obj) => ({ id: obj.id }));

      const variables = {
        id: _.get(getCampaignQuery, "campaign.id"),
        name: _.get(getCampaignQuery, "campaign.name"),
        objectsIds,
      };

      console.log("textXYZ", "UPDATED DB (Campaign)", { variables });

      await updateCampaignMutation({ variables });
    };

    // if we receive instructions to update the object, then just do that
    console.log("UPDATING CAMPAIGN -->", object, action);

    if (action === "update") {
      const { connectTo, ...cleanedObj } = object;

      console.log({ connectTo });

      // get current connections (from db)
      const currentObject = await client.query({
        query: getCampaignObject,
        variables: {
          id: object.id,
        },
      });

      const currentObjectConnections = _.get(
        currentObject,
        "data.object.connectTo",
        []
      );

      console.log({ currentObject, currentObjectConnections });

      // isolate if any should be disconnected
      const toDisconnect = _.filter(
        currentObjectConnections,
        (itm) => _.findIndex(connectTo, (newItm) => itm.id === newItm.id) === -1
      );
      console.log({ toDisconnect });

      // build array

      const variables = {
        ...cleanedObj,
        deleted: object.deleted || false,
        oldCampaignId: object.oldCampaignId || null,
        connectToIds: connectTo ? connectTo.map((obj) => ({ id: obj.id })) : [],
        disconnectIds: toDisconnect.length
          ? toDisconnect.map((obj) => ({ id: obj.id }))
          : [],
      };

      console.log({ variables });

      console.log("UPDATING DB (Object)");

      await updateCampaignObjectMutation({ variables });

      await updateCampaign(this.state.content);

      // if we receive instructions to create an object, then just do that
    } else if (action === "create") {
      const { id } = object;

      let newObject = object;

      console.log("testXYZ", { newObject });

      let defaultObjectSettings = {};

      const returnObject = await triggerAsyncHook(
        "onNodeCreate",
        { type: object.type },
        { item: object },
        {
          createPage: async (variables) => {
            console.log("testXYZ", { variables });

            const { data } = await this.props.createPageMutation({ variables });

            console.log("testXYZ", { data });

            return {
              ..._.omit(newObject, "id"),
              page: {
                ...variables,
                id: data.createPage.id,
              },
            };
          },
          // updateObject: async variables =>
          // await updateCampaignObjectMutation({ variables }),
          updateObjectSettings: (variables) => {
            defaultObjectSettings = variables;
          },
          updatePageSettings: async (variables) =>
            await updatePageSettings({ variables }),
        }
      );

      if (returnObject) {
        newObject = _.head(returnObject);
      }

      console.log("testXYZ", "the page id is...", newObject);

      const campaignId = _.get(getCampaignQuery, "campaign.id", false);

      // create a campaign object
      const variables = {
        ..._.omit(newObject, "id"),
        campaignId,
        ...(_.has(newObject, "page.id") && { pageId: newObject.page.id }),
        settings: {
          ..._.get(newObject, "settings", {}),
          ...defaultObjectSettings,
        },
        connectToIds: object.connectTo
          ? object.connectTo.map((obj) => ({ id: obj.id }))
          : [],
      };

      console.log("testXYZ", { campaignId, variables });

      const { data } = await createCampaignObjectMutation({
        variables,
      });

      console.log("testXYZ", { data });

      // update the canvas
      this.setState(
        (prevState) => {
          const key = prevState.content.findIndex((itm) => itm.id === id);
          const newContent = {
            ...newObject,
            id: _.get(data, "createObject.id", false),
          };

          console.log("testXYZ", { newContent });

          return {
            content: [
              ...prevState.content.slice(0, key),
              newContent,
              ...prevState.content.slice(key + 1),
            ],
          };
        },

        async () => {
          await updateCampaign(this.state.content);
          await getCampaignQuery.refetch({ id: campaignId });
        }
      );
    } else {
      await updateCampaign(this.state.content);
    }
  };

  getCampaignState = (key) => {
    return this.state[key];
  };

  setCampaignState = (prevState, callback) => {
    this.setState(prevState, callback);
  };

  setActiveObject(settings) {
    if (settings && _.get(this.state, "settings.id") !== settings.id) {
      console.log("do setting active settings", settings);
      this.setState({ settings });
    }
  }

  showPopup(popup, popupPayload, popupActions) {
    this.setState({
      showPopup: true,
      PopupComponent: popup,
      popupPayload,
      popupActions,
    });
  }

  render() {
    const { app } = this.context;
    const { show, loading, error, getCampaignQuery } = this.props;
    const {
      showPublish,
      content,
      isShowingStats,
      showPopup,
      PopupComponent,
      popupPayload,
      popupActions,
    } = this.state;

    const campaignProps = {
      show,
      addCanvasObject: this.addCanvasObject,
      removeCanvasObject: this.removeCanvasObject,
      duplicateCanvasObject: this.duplicateCanvasObject,
      updateCanvasObject: this.updateCanvasObject,
      handleItemUpdate: this.handleItemUpdate,
      campaignContent: content,
      disconnectObjects: this.disconnectObjects,
      connectObjects: this.connectObjects,
      activeObject: this.state.settings,
      setActiveObject: this.setActiveObject,
      count: this.state.count,
      domainName: this.state.domainName,
      getCampaignState: this.getCampaignState,
      setCampaignState: this.setCampaignState,
      showPopup: this.showPopup,
    };

    if (getCampaignQuery.loading || loading)
      return (
        <div style={{ textAlign: "center" }}>
          <img src={app.loadingImage} alt="Loading..." />
        </div>
      );

    if (getCampaignQuery.error || error) {
      console.log({ getCampaignQuery });

      return (
        <div style={{ textAlign: "center" }}>
          <img src={app.errorImage} alt="Connection Error" />
        </div>
      );
    }

    const FullCampaign = isShowingStats ? JustCampaignFull : CampaignFull;

    // console.log(this.state.settings)

    return (
      <div style={{ backgroundColor: style.backgroundColor }}>
        {showPublish ? (
          <PublishCampaignSuccess
            {...this.props}
            onClose={() => this.setState({ showPublish: false })}
          />
        ) : null}
        {showPopup ? (
          <PopupComponent
            {...this.props}
            options={popupPayload}
            actions={popupActions}
            onClose={() => this.setState({ showPopup: false })}
          />
        ) : null}
        <Popups {...this.props} />
        <CampaignHeader
          {...this.props}
          activeObject={this.state.settings}
          count={this.state.count}
          domainName={this.state.domainName}
          onPublish={() => this.setState({ showPublish: true })}
          getCampaignState={this.getCampaignState}
          setCampaignState={this.setCampaignState}
        />

        {show === "legend" ? (
          <CampaignLegend {...this.props} {...campaignProps} />
        ) : (
          <FullCampaign {...this.props} {...campaignProps} />
        )}
      </div>
    );
  }
}

export default Campaign;
