package httpapi

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"net/http"
	"strconv"
	"strings"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)

var errInvalidTaskIdempotencyKey = errors.New("invalid Idempotency-Key header")

func optionalTaskIdempotencyKey(r *http.Request) (string, bool, error) {
	values := r.Header.Values("Idempotency-Key")
	if len(values) == 0 {
		return "", false, nil
	}
	if len(values) != 1 {
		return "", false, errInvalidTaskIdempotencyKey
	}
	value := strings.TrimSpace(values[0])
	if value == "" || len(value) > 255 || strings.Contains(value, ",") {
		return "", false, errInvalidTaskIdempotencyKey
	}
	return value, true, nil
}

func taskIdempotencyKeyHash(key string) string {
	digest := sha256.Sum256([]byte(key))
	return hex.EncodeToString(digest[:])
}

func taskIdempotencyRequestHash(kind string, async bool, stream bool, body map[string]any) string {
	payload, _ := json.Marshal(map[string]any{
		"kind": kind, "async": async, "stream": stream, "request": body,
	})
	digest := sha256.Sum256(payload)
	return hex.EncodeToString(digest[:])
}

func writeIdempotentTaskReplay(w http.ResponseWriter, task store.GatewayTask, compatible bool) {
	w.Header().Set("Idempotent-Replayed", "true")
	w.Header().Set("X-Gateway-Task-Id", task.ID)
	if !compatible || task.AsyncMode || (task.Status != "succeeded" && task.Status != "failed" && task.Status != "cancelled") {
		writeTaskAccepted(w, task)
		return
	}
	if task.Status == "succeeded" {
		writeJSON(w, http.StatusOK, task.Result)
		return
	}
	status := taskErrorHTTPStatus(task)
	message := strings.TrimSpace(task.ErrorMessage)
	if message == "" {
		message = strings.TrimSpace(task.Error)
	}
	if message == "" {
		message = "task failed"
	}
	code := strings.TrimSpace(task.ErrorCode)
	if code == "" {
		code = "task_failed"
	}
	writeError(w, status, message, code)
}

func storedTaskErrorStatus(code string) int {
	switch strings.TrimSpace(code) {
	case "storage_write_failed", "storage_read_failed", "storage_config_invalid", "storage_auth_failed":
		return http.StatusServiceUnavailable
	case "binary_result_expired", "result_expired", "result_unavailable":
		return http.StatusGone
	case "pricing_unavailable", "response_chain_unavailable", "billing_hold":
		return http.StatusServiceUnavailable
	case "insufficient_balance":
		return http.StatusPaymentRequired
	case "bad_request", "invalid_parameter", "invalid_previous_response_id", "unsupported_operation":
		return http.StatusBadRequest
	case "no_model_candidate", "cloned_voice_not_found":
		return http.StatusNotFound
	case "gateway_rate_limited", "rate_limit", "platform_cooling_down", "model_cooling_down":
		return http.StatusTooManyRequests
	default:
		if strings.HasPrefix(strings.TrimSpace(code), "http_") {
			if status, err := strconv.Atoi(strings.TrimPrefix(strings.TrimSpace(code), "http_")); err == nil && status >= 400 && status <= 599 {
				return status
			}
		}
		return http.StatusBadGateway
	}
}
