package auditlog

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/tidbcloud/pkgv2/bizerr"
	"go.uber.org/zap"

	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
)

func t(v interface{}) string {
	if v == nil {
		return ""
	}

	// https://go.dev/ref/spec#Types
	switch vv := v.(type) {
	case string:
		return vv
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		return fmt.Sprintf("%d", vv)
	case bool:
		return fmt.Sprintf("%t", vv)
	case float32, float64:
		return fmt.Sprintf("%f", vv)
	case complex64, complex128:
		return fmt.Sprintf("%v", vv)
	case time.Time, *time.Time:
		return fmt.Sprintf("%s", vv)
	case interface{ String() string }:
		return vv.String()
	case error:
		if bz, ok := vv.(bizerr.BizError); ok {
			return fmt.Sprintf("%d, %s", bz.ErrCode(), bz.ErrMsg())
		} else if te, ok := errs.AsTiError(vv); ok {
			return fmt.Sprintf("%d, %s", te.BizCode(), te.ErrMsg())
		} else {
			return fmt.Sprintf("%s", vv)
		}
	default:
		vb, err := json.Marshal(&vv)
		if err != nil {
			tilog.Error(context.TODO(), "failed to marshal value as json", zap.Any("value", vv))
			return fmt.Sprintf("%+v", vv)
		}

		return string(vb)
	}
}

type Details map[string]interface{}

func (d Details) SetError(err error) Details {
	d[KeyError] = err
	return d
}

func (d Details) Build() map[string]string {
	m := make(map[string]string)

	for k, v := range d {
		m[k] = t(v)
	}

	return m
}
