package errors

import (
	"fmt"
	"strings"
)

// ErrorKind categorizes errors by user-actionability.
type ErrorKind string

const (
	KindAuth      ErrorKind = "auth"       // Login/token issues — user can re-login
	KindInput     ErrorKind = "input"      // Parameter issues — user can fix arguments
	KindConfig    ErrorKind = "config"     // Config file issues — user can edit config
	KindNetwork   ErrorKind = "network"    // Network issues — user can check/retry
	KindGit       ErrorKind = "git"        // Git repo issues — user needs correct directory
	KindServer    ErrorKind = "server"     // Server-side error — user should wait or contact admin
	KindNotFound  ErrorKind = "not_found"  // Resource not found — user can check ID
	KindForbidden ErrorKind = "forbidden"  // Permission denied — user can request access
	KindUnknown   ErrorKind = "unknown"    // Unclassified error
)

// CLIError is the unified CLI error type with multi-layered information.
type CLIError struct {
	Kind       ErrorKind // Error category for programmatic handling
	Message    string    // Human-readable description of what went wrong
	Detail     string    // Low-level technical detail (shown in debug mode)
	Suggestion string    // Actionable advice for the user
	Command    string    // The command that triggered the error (e.g., "issue +create")
	Cause      error     // The underlying error
}

func (e *CLIError) Error() string {
	var b strings.Builder

	// Header line: kind + command
	b.WriteString(string(e.Kind))
	b.WriteString(" error")
	if e.Command != "" {
		b.WriteString(" — ")
		b.WriteString(e.Command)
	}

	// Body: message
	if e.Message != "" {
		b.WriteString("\n\n  reason: ")
		b.WriteString(e.Message)
	}

	// Suggestion
	if e.Suggestion != "" {
		b.WriteString("\n  suggestion: ")
		b.WriteString(e.Suggestion)
	}

	// Detail (always included in Error() so users see the raw cause)
	if e.Detail != "" {
		b.WriteString("\n  detail: ")
		b.WriteString(e.Detail)
	}

	return b.String()
}

func (e *CLIError) Unwrap() error {
	return e.Cause
}

// New creates a CLIError with the given parameters.
func New(kind ErrorKind, message, suggestion string) *CLIError {
	return &CLIError{
		Kind:       kind,
		Message:    message,
		Suggestion: suggestion,
	}
}

// Wrap creates a CLIError that wraps an underlying cause.
func Wrap(kind ErrorKind, message, suggestion string, cause error) *CLIError {
	return &CLIError{
		Kind:       kind,
		Message:    message,
		Suggestion: suggestion,
		Cause:      cause,
		Detail:     cause.Error(),
	}
}

// WithCommand sets the command context on the error.
func (e *CLIError) WithCommand(cmd string) *CLIError {
	e.Command = cmd
	return e
}

// InputError is a convenience constructor for parameter errors.
func InputError(message, suggestion string) *CLIError {
	return New(KindInput, message, suggestion)
}

// AuthError is a convenience constructor for authentication errors.
func AuthError(message, suggestion string) *CLIError {
	return New(KindAuth, message, suggestion)
}

// ConfigError creates a config-related error with the config file path in the suggestion.
func ConfigError(message string, cause error) *CLIError {
	return Wrap(KindConfig, message,
		fmt.Sprintf("检查配置文件 %s 是否正确", configPathPlaceholder()), cause)
}

// configPathPlaceholder avoids circular import; the actual path will be resolved
// in output formatting.
func configPathPlaceholder() string {
	return "~/.config/gitlink-cli/config.yaml"
}
