/*
/*
 * Copyright (c) 2024 Augur AI, Inc.
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. 
 * If a copy of the MPL was not distributed with this file, you can obtain one at https://mozilla.org/MPL/2.0/.
 *
 
 * Copyright (c) 2024 Augur AI, Inc.
 *
 * This file is licensed under the Augur AI Proprietary License.
 *
 * Attribution:
 * This work is based on code from https://github.com/hofstadter-io/hof, licensed under the Apache License 2.0.
 */

package cuetils

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/errors"
	"cuelang.org/go/cue/format"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
)

func PrintCue(val cue.Value) (string, error) {
	syn := val.Syntax(
		cue.Final(),
		cue.ResolveReferences(true),
		cue.Definitions(true),
		cue.Hidden(true),
		cue.Optional(true),
		cue.Attributes(true),
		cue.Docs(true),
	)

	bs, err := format.Node(syn)
	if err != nil {
		return "", err
	}

	return string(bs), nil
}

func FormatCue(val cue.Value) (string, error) {
	syn := val.Syntax(
		cue.Final(),
		cue.ResolveReferences(true),
		cue.Concrete(true),
		cue.Definitions(true),
		cue.Hidden(true),
		cue.Optional(true),
		cue.Attributes(true),
		cue.Docs(true),
	)

	bs, err := format.Node(syn)
	if err != nil {
		return "", err
	}

	return string(bs), nil
}

func CueSyntax(val cue.Value, opts []cue.Option) ast.Node {
	if len(opts) > 0 {
		return val.Syntax(opts...)
	}
	return val.Syntax(
		cue.Attributes(true),
		cue.Concrete(false),
		cue.Definitions(true),
		cue.Docs(true),
		cue.Hidden(true),
		cue.Optional(true),
		cue.ResolveReferences(true),
	)
}

func PrintCueValue(val cue.Value) (string, error) {
	node := val.Syntax(
		cue.Final(),
		cue.Attributes(true),
		cue.Concrete(false),
		cue.Definitions(true),
		cue.Docs(true),
		cue.Hidden(true),
		cue.Optional(false),
		cue.ResolveReferences(true),
	)

	bytes, err := format.Node(
		node,
		format.TabIndent(false),
		format.UseSpaces(2),
		format.Simplify(),
	)
	if err != nil {
		return "", err
	}

	return string(bytes), nil
}

func ValueToSyntaxString(val cue.Value, opts ...cue.Option) (string, error) {
	src, err := format.Node(val.Syntax(opts...))
	str := string(src)
	return str, err
}

func (CRT *CueRuntime) ParseCueExpr(expr string) (cue.Value, error) {
	val := CRT.CueContext.CompileString(expr)
	if val.Err() != nil {
		return val, val.Err()
	}
	return val, nil
}

func (CRT *CueRuntime) PrintValue() error {
	// Get top level struct from cuelang
	S, err := CRT.CueValue.Struct()
	if err != nil {
		return err
	}

	iter := S.Fields()
	for iter.Next() {

		label := iter.Label()
		value := iter.Value()
		fmt.Println("  -", label, value)
		for attrKey, attrVal := range value.Attributes(cue.ValueAttr) {
			fmt.Println("  --", attrKey)
			for i := 0; i < 5; i++ {
				str, err := attrVal.String(i)
				if err != nil {
					break
				}
				fmt.Println("  ---", str)
			}
		}
	}

	return nil
}

func getLang() language.Tag {
	loc := os.Getenv("LC_ALL")
	if loc == "" {
		loc = os.Getenv("LANG")
	}
	loc = strings.Split(loc, ".")[0]
	return language.Make(loc)
}

func CueErrorToString(err error) string {

	p := message.NewPrinter(getLang())
	format := func(w io.Writer, format string, args ...interface{}) {
		p.Fprintf(w, format, args...)
	}
	cwd, _ := os.Getwd()
	w := &bytes.Buffer{}

	errors.Print(w, err, &errors.Config{
		Format:  format,
		Cwd:     cwd,
		ToSlash: true,
	})
	// for _, e := range errors.Errors(err) {
	// }

	// ugh, whitespace cleanup...
	// it is relatively reasonable to do this here
	// as this is called in many places, and also
	// called multiple times on the same error via wrapping
	s := w.String()
	noline := ""
	if strings.HasSuffix(s, "\n") {
		noline = "\n"
	}

	return strings.TrimSpace(s) + noline
}

func ExpandCueError(err error) error {
	s := CueErrorToString(err)
	return fmt.Errorf(s)
}

func PrintCueError(err error) {
	s := CueErrorToString(err)
	fmt.Println(s)
}

func (CR *CueRuntime) PrintCueErrors() {
	for _, err := range CR.CueErrors {
		PrintCueError(err)
	}
}

func PrintWarningOrError(isWarning bool, message string) {
	prefix := "Error"
	color := "\033[31m" // Red color for error

	if isWarning {
		prefix = "Warning"
		color = "\033[33m" // Yellow color for warning
	}

	resetColor := "\033[0m"
	fmt.Printf("%s%s:%s %s\n", color, prefix, resetColor, message)
}
