import moment from "moment";
import { all, call, put, select, takeLatest } from "redux-saga/effects";
import { QUERY_SCHEDULE_LOAD_DATA } from "../../graphql/appointments.queries";
import { INSERT_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
import client from "../../utils/GraphQLClient";
import { CalculateLoad, CheckJobBucket } from "../../utils/SSSUtils";
import {
  scheduleLoadFailure,
  scheduleLoadSuccess,
  setProblemJobs,
} from "./application.actions";
import ApplicationActionTypes from "./application.types";

export function* onCalculateScheduleLoad() {
  yield takeLatest(
    ApplicationActionTypes.CALCULATE_SCHEDULE_LOAD,
    calculateScheduleLoad
  );
}
export function* calculateScheduleLoad({ payload: end }) {
  //REMINDER: Moment.js is not immutable. Today WILL change when adjusted.
  const today = moment().startOf("day");
  const state = yield select();
  const buckets = state.user.bodyshop.ssbuckets;

  try {
    const result = yield client.query({
      query: QUERY_SCHEDULE_LOAD_DATA,
      variables: {
        start: today,
        end: end,
      },
    });
    const { prodJobs, arrJobs, compJobs } = result.data;

    const load = {
      productionTotal: {},
      productionHours: 0,
    };

    //Set the current load.
    buckets.forEach((bucket) => {
      load.productionTotal[bucket.id] = { count: 0, label: bucket.label };
    });

    prodJobs.forEach((item) => {
      //Add all of the jobs currently in production to the buckets so that we have a starting point.
      const bucketId = CheckJobBucket(buckets, item);
      load.productionHours =
        load.productionHours +
        item.labhrs.aggregate.sum.mod_lb_hrs +
        item.larhrs.aggregate.sum.mod_lb_hrs;
      if (bucketId) {
        load.productionTotal[bucketId].count =
          load.productionTotal[bucketId].count + 1;
      } else {
        console.log("Uh oh, this job doesn't fit in a bucket!", item);
      }
    });

    arrJobs.forEach((item) => {
      if (!item.scheduled_in)
        console.log("JOB HAS NO SCHEDULED IN DATE.", item);
      const itemDate = moment(item.scheduled_in).format("yyyy-MM-DD");
      if (!!load[itemDate]) {
        load[itemDate].hoursIn =
          (load[itemDate].hoursIn || 0) +
          item.labhrs.aggregate.sum.mod_lb_hrs +
          item.larhrs.aggregate.sum.mod_lb_hrs;
        load[itemDate].jobsIn.push(item);
      } else {
        load[itemDate] = {
          jobsIn: [item],
          jobsOut: [],
          hoursIn:
            item.labhrs.aggregate.sum.mod_lb_hrs +
            item.larhrs.aggregate.sum.mod_lb_hrs,
        };
      }
    });

    let problemJobs = [];
    compJobs.forEach((item) => {
      if (!(item.actual_completion || item.scheduled_completion))
        console.log("JOB HAS NO COMPLETION DATE.", item);

      const inProdJobs = prodJobs.find((p) => p.id === item.id);
      const inArrJobs = arrJobs.find((p) => p.id === item.id);

      if (!(inProdJobs || inArrJobs)) {
        //Job isn't found in production or coming in.
        //is it going today or scheduled to go today?
        if (
          moment(item.actual_completion || item.scheduled_completion).isSame(
            moment(),
            "day"
          )
        ) {
          console.log("Job is going today anyways, ignore it.", item);
          return;
        }

        if (
          moment(item.actual_completion || item.scheduled_completion).isBefore(
            moment(),
            "day"
          )
        ) {
          console.log("Job should have already gone. Ignoring it.", item);
          return;
        }

        problemJobs.push({
          ...item,
          code: "Job is scheduled for completion, but it is not marked in production nor is it an arriving job in this period. Check the scheduled in and completion dates",
        });
        return;
      }

      const itemDate = moment(
        item.actual_completion || item.scheduled_completion
      ).format("yyyy-MM-DD");
      if (!!load[itemDate]) {
        load[itemDate].hoursOut =
          (load[itemDate].hoursOut || 0) +
          item.labhrs.aggregate.sum.mod_lb_hrs +
          item.larhrs.aggregate.sum.mod_lb_hrs;
        load[itemDate].jobsOut.push(item);
      } else {
        load[itemDate] = {
          jobsOut: [item],
          hoursOut:
            item.labhrs.aggregate.sum.mod_lb_hrs +
            item.larhrs.aggregate.sum.mod_lb_hrs,
        };
      }
    });

    console.log(
      "🚀 ~ file: application.sagas.js:160 ~ function*calculateScheduleLoad ~ load.productionTotal",
      load.productionTotal
    );
    //Propagate the expected load to each day.
    const range = Math.round(moment.duration(end.diff(today)).asDays());
    for (var day = 0; day < range; day++) {
      const current = moment(today).add(day, "days").format("yyyy-MM-DD");
      const prev = moment(today)
        .add(day - 1, "days")
        .format("yyyy-MM-DD");
      if (!!!load[current]) {
        load[current] = {};
      }
      if (day === 0) {
        //Starting on day 1. The load is current.
        load[current].expectedLoad = CalculateLoad(
          load.productionTotal,
          buckets,
          load[current].jobsIn || [],
          load[current].jobsOut || []
        );
        load[current].expectedHours =
          load.productionHours +
          (load[current].hoursIn || 0) -
          (load[current].hoursOut || 0);
      } else {
        load[current].expectedLoad = CalculateLoad(
          load[prev].expectedLoad,
          buckets,
          load[current].jobsIn || [],
          load[current].jobsOut || []
        );
        load[current].expectedHours =
          load[prev].expectedHours +
          (load[current].hoursIn || 0) -
          (load[current].hoursOut || 0);
      }
    }
    yield put(setProblemJobs(problemJobs));
    yield put(scheduleLoadSuccess(load));
  } catch (error) {
    yield put(scheduleLoadFailure(error));
  }
}

export function* onInsertAuditTrail() {
  yield takeLatest(
    ApplicationActionTypes.INSERT_AUDIT_TRAIL,
    insertAuditTrailSaga
  );
}

export function* insertAuditTrailSaga({
  payload: { jobid, billid, operation },
}) {
  const state = yield select();
  const bodyshop = state.user.bodyshop;
  const currentUser = state.user.currentUser;

  const variables = {
    auditObj: {
      bodyshopid: bodyshop.id,
      jobid,
      billid,
      operation,
      useremail: currentUser.email,
    },
  };
  yield client.mutate({
    mutation: INSERT_AUDIT_TRAIL,
    variables,
    update(cache, { data }) {
      cache.modify({
        fields: {
          audit_trail(existingAuditTrail, { readField }) {
            const newAuditTrail = cache.writeQuery({
              data: data.insert_audit_trail_one,
              query: INSERT_AUDIT_TRAIL,
              variables,
            });
            return [...existingAuditTrail, newAuditTrail];
          },
        },
      });
    },
  });
}

export function* applicationSagas() {
  yield all([call(onCalculateScheduleLoad), call(onInsertAuditTrail)]);
}
