package main

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

func CheckResults(testCase *TestCase) (bool, error) {
	var result bool = true

	// Check headers
	headerContains, err := HeaderContains(testCase)
	if err != nil {
		return result, err
	}
	result = result && headerContains

	headerEquals, err := HeaderEquals(testCase)
	if err != nil {
		return result, err
	}
	result = result && headerEquals

	// Check body
	bodyContains, err := BodyContains(testCase)
	if err != nil {
		return result, err
	}
	result = result && bodyContains

	bodyHasKeys, err := BodyHasKeys(testCase)
	if err != nil {
		return result, err
	}
	result = result && bodyHasKeys

	return result, err
}

func HeaderContains(testCase *TestCase) (bool, error) {
	for k, v := range testCase.Expect.Header.Contains {
		//fmt.Printf("Header contains check '%s'='%s'\n", k, v)

		if k == "http_status" { // http status is a special case
			statusCode, err := strconv.Atoi(v)
			if err != nil {
				error := fmt.Sprintf("unable to convert http_status expected value '%s' to int : %s", v, err)
				return false, errors.New(error)
			}
			if statusCode != testCase.ResultStatusCode {
				return false, nil
			}
		} else { // any other contains check
			checkFound := false
			for hKey, hVal := range testCase.ResultHeaders {
				if k == hKey {
					//fmt.Printf("Found header key '%s', checking value '%s' matches test definition '%s'\n", hKey, hVal, v)

					for i := range hVal {
						if strings.Contains(hVal[i], v) {
							//fmt.Printf("Found match '%s' = '%s'\n", hVal[i], v)
							checkFound = true
						}
					}
				}
			}

			if !checkFound {
				fmt.Printf("Header Contains Check failed to find expected header key-value of '%s:%s'\n", k, v)
				return false, nil
			}
		}
	}

	return true, nil
}

func HeaderEquals(testCase *TestCase) (bool, error) {

	for k, v := range testCase.Expect.Header.Equals {
		fmt.Printf("Header equals check '%s'='%s'\n", k, v)

		// TODO
	}

	return true, nil
}

func BodyContains(testCase *TestCase) (bool, error) {
	for k, v := range testCase.Expect.Body.Contains {
		fmt.Printf("Body contains check '%s'='%s'\n", k, v)
		fmt.Printf("%+v\n", testCase.ResultBodyMap)

		results := findKey(k, testCase.ResultBodyMap)

		if len(results) > 0 {
			fmt.Printf("Found key '%s': %v\n", k, results)

			checkFound := false
			// search through all the results to see if the expected value is there
			for i := range results {
				if strings.Contains(results[i].(string), v) {
					fmt.Printf("Expected value of '%s' matches\n", v)
					checkFound = true
					break
				}
			}

			if !checkFound {
				fmt.Printf("Body Contains check failed on key '%s', did not find expected value '%s'\n", k, v)
				return false, nil
			}

		} else {
			fmt.Printf("Key '%s' not found\n", k)
		}
	}

	return true, nil
}

func BodyHasKeys(testCase *TestCase) (bool, error) {
	for _, key := range testCase.Expect.Body.HasKeys {

		results := findKey(key, testCase.ResultBodyMap)

		if len(results) > 0 {
			fmt.Printf("Found key '%s' with values %v\n", key, results)
		} else {
			fmt.Printf("Body HasKeys test failed, expected key '%s' not found\n", key)
			return false, nil
		}

		/*
			// If the body response was json then we stored it already when running the test
			if _, ok := testCase.ResultBodyMap[key]; ok {
				//fmt.Printf("Confirmed body has key '%s'\n", key)
			} else {
				fmt.Printf("Body HasKeys test failed, expected key '%s' not found\n", key)
				return false, nil
			}
		*/
	}

	return true, nil
}

// findKey recursively searches map for specified key
func findKey(key string, data interface{}) []interface{} {
	var results []interface{}

	switch v := data.(type) {
	case map[string]interface{}:
		if val, ok := v[key]; ok {
			results = append(results, val)
		}
		for _, value := range v {
			results = append(results, findKey(key, value)...)
		}
	case []interface{}:
		for _, value := range v {
			results = append(results, findKey(key, value)...)
		}
	}
	return results
}
