package api

import (
	"errors"
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/observability/tracing"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

const observedServerErrorKey = "api.observed_server_error"

// ObservedServerError carries the server-side error that caused an HTTP 5xx.
type ObservedServerError struct {
	Err    error
	Status int
}

func observeServerError(ctx *gin.Context, err error, status int) {
	observedErr := errorForObservation(err)
	if ctx == nil || observedErr == nil || status < http.StatusInternalServerError {
		return
	}
	ctx.Set(observedServerErrorKey, ObservedServerError{Err: observedErr, Status: status})

	tracing.RecordHTTPServerError(ginRequestContext(ctx), observedErr, status)
}

// errorForObservation projects the internal error to observe, log, and trace.
// It prefers an explicit HTTPError cause, then the preserved internal
// repository/message-store cause carried by a manage/store CauseAccessor. The
// internal cause is surfaced here (and only here) so repository failures remain
// diagnosable in structured logs, spans, and ObservedServerError without ever
// re-entering the public errors.Is/As classification graph or the public JSON
// problem/prompt text.
func errorForObservation(err error) error {
	var httpErr HTTPError
	if errors.As(err, &httpErr) && httpErr.Cause != nil {
		err = httpErr.Cause
	}
	if internal := storeerr.InternalCause(err); internal != nil {
		return internal
	}
	return err
}

// ObservedErrorWithCause returns the error to log/trace for a failing internal
// Workflow operation: the original public error (whose Error() is the reviewed
// stable problem text) together with the preserved internal repository/
// message-store cause when one is retained. The internal cause is surfaced only
// here — never in the public JSON/prompt — so structured logs keep repository
// failures diagnosable without re-entering the errors.Is/As classification
// graph or projecting public prose.
func ObservedErrorWithCause(err error) (public error, internal error) {
	if err == nil {
		return nil, nil
	}
	return err, storeerr.InternalCause(err)
}

// ObservedServerErrorFromContext returns the observed non-panic HTTP 5xx error,
// if one was recorded while writing the response.
func ObservedServerErrorFromContext(ctx *gin.Context) (ObservedServerError, bool) {
	if ctx == nil {
		return ObservedServerError{}, false
	}
	value, ok := ctx.Get(observedServerErrorKey)
	if !ok {
		return ObservedServerError{}, false
	}
	observed, ok := value.(ObservedServerError)
	if !ok || observed.Err == nil {
		return ObservedServerError{}, false
	}
	return observed, true
}
