import { AbstractOp } from './AbstractOp';
import { find, isArrayReference, formatJsonPointer } from '@jsonjoy.com/json-pointer';
import { isTextNode, isElementNode } from '../util';
import { OPCODE } from '../constants';
/**
 * @category JSON Patch Extended
 */
export class OpMerge extends AbstractOp {
    pos;
    props;
    constructor(path, pos, props) {
        super(path);
        this.pos = pos;
        this.props = props;
    }
    op() {
        return 'merge';
    }
    code() {
        return OPCODE.merge;
    }
    apply(doc) {
        const ref = find(doc, this.path);
        if (!isArrayReference(ref))
            throw new Error('INVALID_TARGET');
        if (ref.key <= 0)
            throw new Error('INVALID_KEY');
        const one = ref.obj[ref.key - 1];
        const two = ref.obj[ref.key];
        const merged = this.merge(one, two);
        ref.obj[ref.key - 1] = merged;
        ref.obj.splice(ref.key, 1);
        return { doc, old: [one, two] };
    }
    merge(one, two) {
        if (typeof one === 'string' && typeof two === 'string')
            return one + two;
        if (typeof one === 'number' && typeof two === 'number')
            return one + two;
        if (isTextNode(one) && isTextNode(two))
            return { ...one, ...two, text: one.text + two.text };
        if (isElementNode(one) && isElementNode(two))
            return { ...one, ...two, children: [...one.children, ...two.children] };
        return [one, two];
    }
    toJson(parent) {
        const op = {
            op: 'merge',
            path: formatJsonPointer(this.path),
            pos: this.pos,
        };
        if (this.props)
            op.props = this.props;
        return op;
    }
    toCompact(parent, verbose) {
        const opcode = verbose ? 'merge' : OPCODE.merge;
        return this.props ? [opcode, this.path, this.pos, this.props] : [opcode, this.path, this.pos];
    }
}
//# sourceMappingURL=OpMerge.js.map