package agent

import (
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/drivercontract"
)

type runtimeStartupError struct {
	deterministic bool
	cause         error
}

func (e *runtimeStartupError) Error() string {
	if e == nil || e.cause == nil {
		return "runtime startup failed"
	}
	return e.cause.Error()
}

func (e *runtimeStartupError) Unwrap() error {
	if e == nil {
		return nil
	}
	return e.cause
}

// IsDeterministicRuntimeStartupError reports whether a launch error is a
// stable configuration/catalog failure that should not be retried.
func IsDeterministicRuntimeStartupError(err error) bool {
	var startupErr *runtimeStartupError
	return errors.As(err, &startupErr) && startupErr.deterministic
}

// NewDeterministicRuntimeStartupError marks a known launch/catalog
// configuration failure for dispatcher lifecycle policy.
func NewDeterministicRuntimeStartupError(err error) error {
	if err == nil {
		return nil
	}
	return &runtimeStartupError{deterministic: true, cause: err}
}

func piStartupFailure(err error) error {
	if err == nil {
		return nil
	}
	var startupErr *runtimeStartupError
	if errors.As(err, &startupErr) {
		return err
	}
	return &runtimeStartupError{
		deterministic: isDeterministicPiStartupError(err),
		cause:         err,
	}
}

// piStartupFailureWithStderr preserves the original launch/transport error
// while adding the bounded child-process diagnostic captured on stderr. This
// is deliberately only a diagnostic enrichment: deterministic classification
// still goes through the narrow Pi catalog/configuration matcher below.
func piStartupFailureWithStderr(err error, stderr *capturingPrefixWriter) error {
	if err == nil {
		return nil
	}
	tail := ""
	if stderr != nil {
		tail = strings.TrimSpace(stderr.Tail())
	}
	if tail != "" {
		err = fmt.Errorf("%w\npi stderr:\n%s", err, tail)
	}
	return piStartupFailure(err)
}

func newSessionResumeFallback(reason, operation string) *drivercontract.SessionResumeFallback {
	return &drivercontract.SessionResumeFallback{
		Reason:    strings.TrimSpace(reason),
		Operation: strings.TrimSpace(operation),
	}
}

func isDeterministicPiStartupError(err error) bool {
	if err == nil {
		return false
	}
	text := strings.ToLower(strings.TrimSpace(err.Error()))
	if (strings.Contains(text, "model") || strings.Contains(text, "provider")) &&
		strings.Contains(text, "not found") {
		return true
	}
	for _, marker := range []string{
		"unknown provider",
		"unknown model",
		"model not found",
		"no models available",
		"invalid models.json",
		"failed to load models",
		"unsupported api",
		"inline provider",
		"models path",
		"models catalog",
		"installed models",
		"environment key",
		"safe directory",
		"safe regular file",
		"invalid session id",
	} {
		if strings.Contains(text, marker) {
			return true
		}
	}
	return false
}
