package utils

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/google/cel-go/cel"
	"github.com/google/cel-go/checker/decls"
)

func ParseHTTPBodyToCElVars(bs []byte) (map[string]any, cel.EnvOption, error) {
	key := "body"
	output := make(map[string]any)
	bs = bytes.TrimSpace(bs)
	if len(bs) == 0 {
		output[key] = make(map[string]any)
		envOpt := cel.Declarations(decls.NewVar(key, decls.NewMapType(decls.String, decls.Dyn)))
		return output, envOpt, nil
	}

	var envOpt cel.EnvOption
	switch bs[0] {
	case 0x5b:
		data := make([]any, 0)
		if err := json.Unmarshal(bs, &data); err != nil {
			return nil, nil, err
		}
		output[key] = data
		envOpt = cel.Declarations(decls.NewVar(key, decls.NewListType(decls.Dyn)))
	case 0x7b:
		data := make(map[string]any)
		if err := json.Unmarshal(bs, &data); err != nil {
			return nil, nil, err
		}
		output[key] = data
		envOpt = cel.Declarations(decls.NewVar(key, decls.NewMapType(decls.String, decls.Dyn)))
	default:
		return nil, nil, fmt.Errorf("http response body is not valid json object or array")
	}
	return output, envOpt, nil
}

func ParseCMDOutputToCElVars(bs []byte) (map[string]any, cel.EnvOption, error) {
	key := "output"
	output := make(map[string]any)
	var envOpt cel.EnvOption = func(e *cel.Env) (*cel.Env, error) { return e, nil }
	s := string(bs)
	output[key] = s
	if len(bs) <= 2 {
		// a valid json string must be `{}` or `[]` which at least 2 bytes
		// nothing to parse and return directly with no error
		return output, envOpt, nil
	}
	// trim warning message before process the json string
	bs = bytes.TrimSpace(bs)
	if len(bs) > 0 && bs[0] != 0x5b && bs[0] != 0x7b {
		i := bytes.IndexByte(bs, 0x5b)
		j := bytes.IndexByte(bs, 0x7b)
		jsonStart := -1
		switch {
		case i >= 0 && j >= 0:
			jsonStart = min(i, j)
		case i >= 0:
			jsonStart = i
		case j >= 0:
			jsonStart = j
		}
		if jsonStart < 0 {
			fmt.Printf("%s is not a valid json string, cannot found `{` or `[`, retrun directly", s)
			envOpt = cel.Declarations(decls.NewVar(key, decls.String))
			return output, envOpt, nil
		}
		bs = bs[jsonStart:]
	}
	switch bs[0] {
	case 0x5b:
		// '[' -> 0x5b is array case
		data := make([]any, 0)
		if err := json.Unmarshal(bs, &data); err != nil {
			return nil, nil, err
		}
		output[key] = data
		envOpt = cel.Declarations(decls.NewVar(key, decls.NewListType(decls.Dyn)))
	case 0x7b:
		// '{' -> 0x7b is map case
		data := make(map[string]any)
		if err := json.Unmarshal(bs, &data); err != nil {
			return nil, nil, err
		}
		output[key] = data
		envOpt = cel.Declarations(
			decls.NewVar(key, decls.NewMapType(decls.String, decls.Dyn)),
		)
	}
	return output, envOpt, nil
}
