package apierror_test

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/veylant/ia-gateway/internal/apierror"
)

func TestNewAuthError(t *testing.T) {
	e := apierror.NewAuthError("bad token")
	assert.Equal(t, http.StatusUnauthorized, e.HTTPStatus)
	assert.Equal(t, "authentication_error", e.Type)
	assert.Equal(t, "bad token", e.Message)
	assert.NotEmpty(t, e.Code)
}

func TestNewForbiddenError(t *testing.T) {
	e := apierror.NewForbiddenError("no access")
	assert.Equal(t, http.StatusForbidden, e.HTTPStatus)
	assert.Equal(t, "permission_error", e.Type)
}

func TestNewBadRequestError(t *testing.T) {
	e := apierror.NewBadRequestError("missing model")
	assert.Equal(t, http.StatusBadRequest, e.HTTPStatus)
	assert.Equal(t, "invalid_request_error", e.Type)
}

func TestNewUpstreamError(t *testing.T) {
	e := apierror.NewUpstreamError("OpenAI down")
	assert.Equal(t, http.StatusBadGateway, e.HTTPStatus)
	assert.Equal(t, "api_error", e.Type)
}

func TestNewRateLimitError(t *testing.T) {
	e := apierror.NewRateLimitError("too many requests")
	assert.Equal(t, http.StatusTooManyRequests, e.HTTPStatus)
	assert.Equal(t, "rate_limit_error", e.Type)
	assert.Equal(t, 1, e.RetryAfterSec, "NewRateLimitError must set RetryAfterSec=1 (RFC 6585)")
}

func TestWriteError_RetryAfter_SetWhenPresent(t *testing.T) {
	rec := httptest.NewRecorder()
	apierror.WriteError(rec, apierror.NewRateLimitError("slow down"))
	assert.Equal(t, "1", rec.Header().Get("Retry-After"))
}

func TestWriteError_NoRetryAfter_WhenZero(t *testing.T) {
	rec := httptest.NewRecorder()
	apierror.WriteError(rec, apierror.NewAuthError("denied"))
	assert.Empty(t, rec.Header().Get("Retry-After"))
}

func TestWriteErrorWithRequestID_SetsHeader(t *testing.T) {
	rec := httptest.NewRecorder()
	apierror.WriteErrorWithRequestID(rec, apierror.NewAuthError("denied"), "req-abc-123")
	assert.Equal(t, "req-abc-123", rec.Header().Get("X-Request-Id"))
	assert.Equal(t, http.StatusUnauthorized, rec.Code)
}

func TestWriteErrorWithRequestID_EmptyID_NoHeader(t *testing.T) {
	rec := httptest.NewRecorder()
	apierror.WriteErrorWithRequestID(rec, apierror.NewAuthError("denied"), "")
	assert.Empty(t, rec.Header().Get("X-Request-Id"))
}

func TestNewTimeoutError(t *testing.T) {
	e := apierror.NewTimeoutError("upstream timed out")
	assert.Equal(t, http.StatusGatewayTimeout, e.HTTPStatus)
}

func TestNewInternalError(t *testing.T) {
	e := apierror.NewInternalError("unexpected panic")
	assert.Equal(t, http.StatusInternalServerError, e.HTTPStatus)
}

func TestAPIError_Error(t *testing.T) {
	e := apierror.NewAuthError("some message")
	assert.Equal(t, "some message", e.Error())
}

func TestWriteError_SetsStatusAndContentType(t *testing.T) {
	rec := httptest.NewRecorder()
	apierror.WriteError(rec, apierror.NewAuthError("denied"))

	assert.Equal(t, http.StatusUnauthorized, rec.Code)
	assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
}

func TestWriteError_BodyIsOpenAIEnvelope(t *testing.T) {
	rec := httptest.NewRecorder()
	apierror.WriteError(rec, apierror.NewRateLimitError("slow down"))

	var body struct {
		Error struct {
			Type    string `json:"type"`
			Message string `json:"message"`
			Code    string `json:"code"`
		} `json:"error"`
	}
	require.NoError(t, json.NewDecoder(rec.Body).Decode(&body))
	assert.Equal(t, "rate_limit_error", body.Error.Type)
	assert.Equal(t, "slow down", body.Error.Message)
	assert.NotEmpty(t, body.Error.Code)
}
