package testhelper

import (
	"encoding/json"
	"fmt"
)

type JSONComparer struct {
	result      map[string]interface{}
	IgnoreProps []string
}

func (j *JSONComparer) Close() {
	j.result = nil
	j.IgnoreProps = nil
}

func (j *JSONComparer) GetDiff(reference []byte, compare []byte) (map[string]interface{}, error) {
	j.result = map[string]interface{}{}
	objRef := map[string]interface{}{}
	objComp := map[string]interface{}{}

	err := json.Unmarshal(reference, &objRef)
	if err != nil {
		return j.result, err
	}

	err = json.Unmarshal(compare, &objComp)
	if err != nil {
		return j.result, err
	}

	j.compare("", objRef, objComp)

	return j.result, nil
}

func (j *JSONComparer) isMap(obj interface{}) (converted map[string]interface{}, isMap bool) {
	converted, isMap = obj.(map[string]interface{})
	return
}

func (j *JSONComparer) isIgnored(key string) bool {
	for _, ignored := range j.IgnoreProps {
		if ignored == key {
			return true
		}
	}
	return false
}

func (j JSONComparer) processMap(key string, refVal interface{}, compVal interface{}) bool {
	if convRef, isRefMap := j.isMap(refVal); isRefMap {
		if convCom, isComMap := j.isMap(compVal); isComMap {
			j.compare(fmt.Sprintf("%s.", key), convRef, convCom)
			return true
		}
	}
	return false
}

func (j *JSONComparer) compare(prefix string, reference map[string]interface{}, compare map[string]interface{}) {
	for key, refVal := range reference {
		if j.isIgnored(key) {
			continue
		}
		if comVal, ok := compare[key]; !ok {
			j.result[key] = "missing"
		} else {
			if j.processMap(key, refVal, comVal) {
				continue
			}
			if comVal != refVal {
				j.result[prefix+key] = refVal
			}
		}
	}
}
