package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"unicode/utf8"

	"github.com/vmihailenco/msgpack/v5"
	"github.com/vmihailenco/msgpack/v5/msgpcode"
)

// unpackMap 在任何業務欄位 materialization 前關閉 MessagePack 結構的歧義。
func unpackMap(b []byte) (map[string]any, error) {
	dec := msgpack.NewDecoder(bytes.NewReader(b))
	dec.SetMapDecoder(func(dec *msgpack.Decoder) (any, error) {
		return decodeUniqueStringMap(dec, len(b))
	})
	out, err := dec.DecodeInterface()
	if err != nil {
		return nil, err
	}
	if _, err := dec.DecodeInterface(); !errors.Is(err, io.EOF) {
		if err == nil {
			return nil, fmt.Errorf("MessagePack contains a trailing value")
		}
		return nil, fmt.Errorf("decode trailing MessagePack value: %w", err)
	}
	if err := validateUTF8(out); err != nil {
		return nil, err
	}
	result, ok := out.(map[string]any)
	if !ok || result == nil {
		return nil, fmt.Errorf("MessagePack value is not a map")
	}
	return result, nil
}

func decodeUniqueStringMap(dec *msgpack.Decoder, maxEntries int) (any, error) {
	length, err := dec.DecodeMapLen()
	if err != nil {
		return nil, err
	}
	if length < 0 || length > maxEntries {
		return nil, fmt.Errorf("MessagePack map declares too many entries")
	}
	result := make(map[string]any, length)
	for range length {
		code, err := dec.PeekCode()
		if err != nil {
			return nil, err
		}
		if !msgpcode.IsString(code) {
			return nil, fmt.Errorf("MessagePack map key is not a string")
		}
		key, err := dec.DecodeString()
		if err != nil {
			return nil, fmt.Errorf("MessagePack map key is not a string: %w", err)
		}
		if !utf8.ValidString(key) {
			return nil, fmt.Errorf("MessagePack map key is not valid UTF-8")
		}
		if _, exists := result[key]; exists {
			return nil, fmt.Errorf("MessagePack map contains duplicate key %q", key)
		}
		val, err := dec.DecodeInterface()
		if err != nil {
			return nil, err
		}
		result[key] = val
	}
	return result, nil
}

func validateUTF8(val any) error {
	switch typed := val.(type) {
	case string:
		if !utf8.ValidString(typed) {
			return fmt.Errorf("MessagePack string is not valid UTF-8")
		}
	case map[string]any:
		for _, item := range typed {
			if err := validateUTF8(item); err != nil {
				return err
			}
		}
	case []any:
		for _, item := range typed {
			if err := validateUTF8(item); err != nil {
				return err
			}
		}
	}
	return nil
}

func validateExactFields(val map[string]any, reqFields, optFields []string) error {
	allowed := make(map[string]struct{}, len(reqFields)+len(optFields))
	for _, field := range reqFields {
		allowed[field] = struct{}{}
		if _, ok := val[field]; !ok {
			return fmt.Errorf("closed map is missing required field %q", field)
		}
	}
	for _, field := range optFields {
		allowed[field] = struct{}{}
	}
	for field := range val {
		if _, ok := allowed[field]; !ok {
			return fmt.Errorf("closed map contains unknown field %q", field)
		}
	}
	return nil
}

// restoreRawFields 先驗完全部 placeholder，再一次性物化，避免失敗時留下半更新 body。
func restoreRawFields(body map[string]any, value any) error {
	var raw [][]byte
	if value != nil {
		rawVals, ok := value.([]any)
		if !ok {
			return fmt.Errorf("KPS raw field is not an array")
		}
		raw = make([][]byte, len(rawVals))
		for i, item := range rawVals {
			raw[i], ok = item.([]byte)
			if !ok {
				return fmt.Errorf("KPS raw[%d] is not binary", i)
			}
		}
	}
	if err := validateRawMap(body, raw); err != nil {
		return err
	}
	materializeRawMap(body, raw)
	return nil
}

func validateRawMap(value map[string]any, raw [][]byte) error {
	for key, item := range value {
		if strings.HasSuffix(key, "__") {
			field := strings.TrimSuffix(key, "__")
			if field == "" {
				return fmt.Errorf("KPS raw placeholder has an empty field name")
			}
			if _, exists := value[field]; exists {
				return fmt.Errorf("KPS raw placeholder for %q collides with a logical field", field)
			}
			index, ok := asInt(item)
			if !ok || index < 0 || index >= len(raw) {
				return fmt.Errorf("KPS raw index for %q is invalid", key)
			}
			continue
		}
		if err := validateRawValue(item, raw); err != nil {
			return err
		}
	}
	return nil
}

func validateRawValue(val any, raw [][]byte) error {
	switch typed := val.(type) {
	case map[string]any:
		return validateRawMap(typed, raw)
	case []any:
		for _, item := range typed {
			if err := validateRawValue(item, raw); err != nil {
				return err
			}
		}
	}
	return nil
}

func materializeRawMap(val map[string]any, raw [][]byte) {
	for key, item := range val {
		if strings.HasSuffix(key, "__") {
			field := strings.TrimSuffix(key, "__")
			index, _ := asInt(item)
			delete(val, key)
			val[field] = raw[index]
			continue
		}
		materializeRawValue(item, raw)
	}
}

func materializeRawValue(val any, raw [][]byte) {
	switch typed := val.(type) {
	case map[string]any:
		materializeRawMap(typed, raw)
	case []any:
		for _, item := range typed {
			materializeRawValue(item, raw)
		}
	}
}
