/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2026, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////

import _ from 'lodash';
import {
  SCHEMA_STATE_ACTIONS, getDepChange,
} from './common';

const getDeferredDepChange = (currPath, newState, oldState, action) => {
  if(action.deferredDepChange) {
    return action.deferredDepChange(currPath, newState, {
      type: action.type,
      path: action.path,
      value: action.value,
      depChange: action.depChange,
      oldState: _.cloneDeep(oldState),
    });
  }
};

/*
 * The main function which manipulates the session state based on actions.
 *
 * The state is managed based on path array of a particular key.
 * For Eg. if the state is
 * {
 *   key1: {
 *     ckey1: [
 *       {a: 0, b: 0},
 *       {a: 1, b: 1}
 *     ]
 *   }
 * }
 *
 * The path for b in first row will be '[key1, ckey1, 0, b]'.
 * The path for second row of ckey1 will be '[key1, ckey1, 1]'.
 *
 * The path for key1 is '[key1]'.
 * The state starts with path '[]'.
 */
export const sessDataReducer = (state, action) => {
  let data = _.cloneDeep(state);
  let rows, cid, deferredList;
  data.__deferred__ = data.__deferred__ || [];

  switch(action.type) {
  case SCHEMA_STATE_ACTIONS.INIT:
    data = action.payload;
    break;

  case SCHEMA_STATE_ACTIONS.BULK_UPDATE:
    rows = _.get(data, action.path) || [];
    rows.forEach((row) => { row[action.id] = false; });
    _.set(data, action.path, rows);
    break;

  case SCHEMA_STATE_ACTIONS.SET_VALUE:
    _.set(data, action.path, action.value);
    // If there is any dep listeners get the changes.
    data = getDepChange(action.path, data, state, action);
    deferredList = getDeferredDepChange(
      action.path, _.cloneDeep(data), state, action
    );
    data.__deferred__ = deferredList || [];
    break;

  case SCHEMA_STATE_ACTIONS.ADD_ROW:
    // Create id to identify a row uniquely, usefull when getting diff.
    cid = _.uniqueId('c');
    action.value['cid'] = cid;

    if (action.addOnTop) {
      rows = [].concat(action.value).concat(_.get(data, action.path) || []);
    } else {
      rows = (_.get(data, action.path) || []).concat(action.value);
    }

    _.set(data, action.path, rows);

    // If there is any dep listeners get the changes.
    data = getDepChange(action.path, data, state, action);

    break;

  case SCHEMA_STATE_ACTIONS.DELETE_ROW:
    rows = _.get(data, action.path)||[];
    rows.splice(action.value, 1);

    _.set(data, action.path, rows);

    // If there is any dep listeners get the changes.
    data = getDepChange(action.path, data, state, action);

    break;

  case SCHEMA_STATE_ACTIONS.MOVE_ROW: {
    rows = _.get(data, action.path)||[];
    let row = rows[action.oldIndex];
    rows.splice(action.oldIndex, 1);
    rows.splice(action.newIndex, 0, row);

    _.set(data, action.path, rows);

    break;
  }
  case SCHEMA_STATE_ACTIONS.CLEAR_DEFERRED_QUEUE:
    data.__deferred__ = [];
    return data;

  case SCHEMA_STATE_ACTIONS.DEFERRED_DEPCHANGE:
    data = getDepChange(action.path, _.cloneDeep(data), state, action);
    break;
  }

  data.__changeId = (data.__changeId || 0) + 1;

  return data;
};

