import * as d3a from 'd3-array';
import { duration_to_ms, ms_to_duration, normalise_duration, add_durations } from '@/lib/durations';

/** Fetch projects from server
 */
async function refreshProjects ({commit, dispatch, state, rootState}) {

  if (state.loading) {
    commit('abortProjectsLoading');
  }

  commit('setProjectsLoading');
  const tstamp = new Date();
  const pid = rootState.project.projectId;
  const url = `/project`;
  const init = {
    cache: "reload",
    signal: state.loading.signal
  };
  const res = await dispatch('api', [url, init, null, {silent:true}]);

  if (res) {
    for (let index in res) {
      const project = res[index];

      if (!project.pid) {
        console.warn("Project has no Project ID!");
        continue;
      }

      const url = `/project/${project.pid}/summary`;
      const init = {};
      const summary = await dispatch('api', [url, init, null, {silent:true}]);
      if (summary) {
        res[index] = {...project, ...summary};
      }
    }
    commit('setProjects', res);
    commit('setProjectsTimestamp', tstamp);
  }
  commit('clearProjectsLoading');
  dispatch('prepareGroups');
}

/** Return a subset of projects from state.projects
 */
async function getProjects ({commit, dispatch, state}, [{pid, name, schema, group, sortBy, sortDesc, itemsPerPage, page, text}] = [{}]) {
  let filteredProjects = [...state.projects];

  if (sortBy) {

    sortBy.forEach( (key, idx) => {
      filteredProjects.sort( (el0, el1) => {
        const a = el0?.[key];
        const b = el1?.[key];
        if (a < b) {
          return -1;
        } else if (a > b) {
          return 1;
        } else if (a == b) {
          return 0;
        } else if (a && !b) {
          return 1;
        } else if (!a && b) {
          return -1;
        } else {
          return 0;
        }
      });
      if (sortDesc && sortDesc[idx] === true) {
        filteredProjects.reverse();
      }
    });

  }

  if (pid) {
    filteredProjects = filteredProjects.filter( project => project.pid == pid );
  }

  if (name) {
    filteredProjects = filteredProjects.filter( project => project.name.toLowerCase().includes(name.toLowerCase()) );
  }

  if (schema) {
    filteredProjects = filteredProjects.filter( project => project.schema.toLowerCase().includes(schema.toLowerCase()) );
  }

  if (group) {
    filteredProjects = filteredProjects.filter( project => project.groups.find(g => g.toLowerCase() == group.toLowerCase()) );
  }

  if (text) {
    const tstampFilter = (value, search, item) => {
      return search?.length >= 5 && textFilter(value, search, item);
    };

    const numberFilter = (value, search, item) => {
      return value == search;
    };

    const textFilter = (value, search, item) => {
      return String(value).toLowerCase().includes(search.toLowerCase());
    };

    const arrayFilter = (elementFilter) => {
      return (value, search, item) => value.some(element => elementFilter(element, search, item));
    };

    const searchFunctions = {
      pid: textFilter,
      name: textFilter,
      group: arrayFilter(textFilter)
    };

    filteredProjects = filteredProjects.filter ( project => {
      for (let key in searchFunctions) {
        const fn = searchFunctions[key];
        if (fn(project[key], text, project)) {
          return true;
        }
      }
      return false;
    });
  }

  const count = filteredProjects.length;

  if (itemsPerPage && itemsPerPage > 0) {
    const offset = (page > 0)
      ? (page-1) * itemsPerPage
      : 0;

    filteredProjects = filteredProjects.slice(offset, offset+itemsPerPage);
  }

  return {projects: filteredProjects, count};
}



async function prepareGroups ({commit, dispatch, state, rootState}) {
  const groups = {};

  for (const project of state.projects) {

    if (!project.prod_distance) {
      // This project has no production data (either not started yet
      // or production data has not been imported) so we skip it.
      continue;
    }

    if (!project.prod_duration.days) {
      project.prod_duration = normalise_duration(project.prod_duration);
    }

    for (const name of project.groups) {
      if (!(name in groups)) {
        groups[name] = {
          group: name,
          num_projects: 0,
          lines: 0,
          points: 0,
          sequences: 0,
          // Shots:
          prime: 0,
          other: 0,
          ntba: 0,
          prod_duration: {
            days: 0,
            hours: 0,
            minutes: 0,
            seconds: 0,
            milliseconds: 0
          },
          prod_distance: 0,
          shooting_rate: [],
          projects: []
        };
      }
      const group = groups[name];

      group.num_projects++;
      group.lines = Math.max(group.lines, project.lines); // In case preplots changed
      group.points = Math.max(group.points, project.total); // Idem
      group.sequences += project.seq_final;
      group.prime += project.prime;
      group.other += project.other;
      //group.ntba += project.ntba;
      group.prod_duration = add_durations(group.prod_duration, project.prod_duration);
      group.prod_distance += project.prod_distance;
      group.shooting_rate.push(project.shooting_rate);
      group.projects.push(project);
    }
  }

  const grouplist = [];
  for (const group of Object.values(groups)) {
    group.shooting_rate_mean = d3a.mean(group.shooting_rate);
    group.shooting_rate_sd = d3a.deviation(group.shooting_rate);
    delete group.shooting_rate;

    grouplist.push(group);
  }

  commit('setGroups', grouplist);

}

async function getGroups({commit, dispatch, state, rootState}) {
  if (!state.groups.length) {
    await dispatch('refreshProjects');
  }

  return state.groups;
}


export default { refreshProjects, getProjects, prepareGroups, getGroups };
