package global_client

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
)

type ErrorResponse struct {
	Code    int           `json:"code"`
	Message string        `json:"message"`
	Details []ErrorDetail `json:"details"`
}

func (resp *ErrorResponse) Error() string {
	return fmt.Sprintf("{code: %d, message: %s}", resp.Code, resp.Message)
}

type ErrorDetail struct {
	Type       string         `json:"@type"`
	Properties map[string]any `json:"-"`
}

func (p ErrorDetail) MarshalJSON() ([]byte, error) {
	// Turn p into a map
	type ErrorDetail_ ErrorDetail // prevent recursion
	bytes, _ := json.Marshal(ErrorDetail_(p))

	var m map[string]json.RawMessage
	_ = json.Unmarshal(bytes, &m)

	// Add Properties to the map, possibly overriding struct fields
	for k, v := range p.Properties {
		// if overriding struct fields is not acceptable:
		if _, ok := m[k]; ok {
			continue
		}
		bytes, _ = json.Marshal(v)
		m[k] = bytes
	}

	return json.Marshal(m)
}

func (p *ErrorDetail) UnmarshalJSON(data []byte) error {
	// unmarshal struct fields
	type ErrorDetail_ ErrorDetail // prevent recursion
	p2 := ErrorDetail_{}
	err := json.Unmarshal(data, &p2)
	if err != nil {
		return err
	}

	// unmarshal as map
	var m map[string]any
	err = json.Unmarshal(data, &m)
	if err != nil {
		return err
	}

	// remove struct field in from map
	rt := reflect.TypeOf(ErrorDetail{})
	for i := 0; i < rt.NumField(); i++ {
		f := rt.Field(i)
		tag := strings.Split(f.Tag.Get("json"), ",")[0] // use split to ignore tag "options" like omitempty, etc.
		if tag != "-" {
			delete(m, tag)
		}
	}

	// set result
	*p = ErrorDetail(p2)
	p.Properties = m
	return nil
}

type BillingErrorResponse struct {
	Code         string `json:"code"`
	Message      string `json:"message"`
	ErrorMessage string `json:"error"`
}

func (resp *BillingErrorResponse) Error() string {
	return resp.ErrorMessage
}
