// Copyright (c) 2026 Alibaba Group and its affiliates

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/dashscope/dashscope-confidential-maas/sdk"

	"github.com/google/uuid"
)

// writeOpenAIError writes a ModelStudio-style error envelope.
// type/code default by HTTP status: 4xx -> invalid_request_error/invalid_parameter_error,
// 5xx -> api_error/internal_error. Pass code="" to use the default.
// requestID is the bare UUID used as the SDK task_id; pass "" to generate one.
func writeOpenAIError(w http.ResponseWriter, status int, message, code, requestID string) {
	errType := "api_error"
	defaultCode := "internal_error"
	if status >= http.StatusBadRequest && status < http.StatusInternalServerError {
		errType = "invalid_request_error"
		defaultCode = "invalid_parameter_error"
	}
	if code == "" {
		code = defaultCode
	}
	if requestID == "" {
		requestID = uuid.New().String()
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(openAIErrorResponse{
		Error: openAIError{
			Message: message,
			Type:    errType,
			Param:   nil,
			Code:    code,
		},
		ID:        "chatcmpl-" + requestID,
		RequestID: requestID,
	})
}

func writeSDKError(w http.ResponseWriter, err error, requestID string) {
	var valErr *sdk.ValidationError
	if errors.As(err, &valErr) {
		writeOpenAIError(w, http.StatusBadRequest, valErr.Message, "", requestID)
		return
	}
	var apiErr *sdk.APIError
	if errors.As(err, &apiErr) {
		status := apiErr.StatusCode
		if status == 0 {
			status = http.StatusInternalServerError
		}
		writeOpenAIError(w, status, apiErr.Message, "", requestID)
		return
	}
	writeOpenAIError(w, http.StatusInternalServerError, "inference error: "+err.Error(), "", requestID)
}

// jsonUnmarshalMessage rewrites Go's json decode errors into ModelStudio-style
// "Type of <field> should be <type>" diagnostics. Non-type errors fall back
// to the original error text.
func jsonUnmarshalMessage(err error) string {
	var typeErr *json.UnmarshalTypeError
	if errors.As(err, &typeErr) && typeErr.Field != "" {
		if typeErr.Field == "messages" {
			return "message must be json_object"
		}
		if typeErr.Field == "max_length" {
			return "max_length must be a positive integer"
		}
		if typeErr.Field == "temperature" {
			return "'temperature' must be Float"
		}
		return fmt.Sprintf("Type of %s should be %s", typeErr.Field, goTypeToModelStudio(typeErr.Type.String()))
	}
	return "invalid JSON: " + err.Error()
}

func goTypeToModelStudio(t string) string {
	switch t {
	case "float64", "float32":
		return "float"
	case "int", "int8", "int16", "int32", "int64",
		"uint", "uint8", "uint16", "uint32", "uint64":
		return "integer"
	case "bool":
		return "boolean"
	case "string":
		return "string"
	}
	return t
}
