const deepDiff = require('deep-diff');
const R = require('ramda');

const KIND_ADDED = 'N';
const KIND_ARRAY = 'A';
const KIND_DELETED = 'D';
const KIND_EDITED = 'E';

class ObjectDiffer {

    diff(objLeft, objRight) {
        const diffs = deepDiff(objLeft, objRight);

        // verify change happened in an array

        return diffs.map(diff => {
            const isNestedChange = diff.path.length > 2;
            const isArrayElementChange = R.any(R.is(Number), diff.path);
            if (!isNestedChange || !isArrayElementChange || diff.kind === KIND_ARRAY || diff.kind === KIND_EDITED || diff.kind === KIND_DELETED) {
                return diff;
            }

            return this._createDiffForChangedArrayElement(objLeft, objRight, diff.path, diff);
        }).reduce((diffs, diff) => {
            const isArrayElementChange = R.any(R.is(Number), diff.path);
            if ((diff.kind === KIND_DELETED || diff.kind === KIND_ARRAY || diff.kind === KIND_EDITED) && isArrayElementChange/*TODO && R.last(diffs) ===  diff*/) {
                const diffAdapted = this._createDiffForChangedArrayElement(objLeft, objRight, diff.path, diff);
                if (R.equals(
                        R.dissocPath(['item','path'], R.dissocPath(['item', 'lhs'], diffAdapted)),
                        R.dissocPath(['item','path'], R.dissocPath(['item', 'lhs'], R.last(diffs) || {}))
                    )
                ){
                    return diffs;
                }
                return R.append(diffAdapted, diffs);
            }

            return R.append(diff, diffs);
        }, []);
    }

    _createDiffForChangedArrayElement(objLeft, objRight, path, diff) {
        const elementPosition = R.findLastIndex(R.is(Number), path);
        const pathToElement = R.slice(0, elementPosition + 1, path);

        const elementModifiedLeft = R.path(pathToElement, objLeft);
        const elementModifiedRight = R.path(pathToElement, objRight);

        return {
            "kind": KIND_ARRAY,
            "path": pathToElement,
            "item": R.merge(diff, {
                lhs: elementModifiedLeft,
                rhs: elementModifiedRight,
                path: R.slice(elementPosition + 1, Infinity, path)
            })
        };
    }
}

module.exports = new ObjectDiffer();