package inbox

import (
	"errors"
	"fmt"
	"io"
	"strings"

	"ai-workflow-skill/internal/protocol"
	"ai-workflow-skill/internal/store"
)

func Execute(args []string, stdout, stderr io.Writer) int {
	cmd := NewRootCmd()
	cmd.SetOut(stdout)
	cmd.SetErr(stderr)
	cmd.SetArgs(args)

	if err := cmd.Execute(); err != nil {
		jsonOutput := hasJSONFlag(args)
		renderError(stdout, stderr, jsonOutput, err)
		return exitCodeForError(err)
	}

	return 0
}

func exitCodeForError(err error) int {
	var cliErr *protocol.CLIError
	if errors.As(err, &cliErr) {
		return cliErr.ExitCode
	}

	switch {
	case isUsageError(err):
		return 30
	case errors.Is(err, store.ErrLeaseConflict):
		return 20
	case errors.Is(err, store.ErrThreadNotFound), errors.Is(err, store.ErrMessageNotFound):
		return 40
	case errors.Is(err, store.ErrInvalidInput), errors.Is(err, store.ErrInvalidState), errors.Is(err, store.ErrNoActiveLease):
		return 30
	default:
		return 50
	}
}

func errorCodeForError(err error) string {
	var cliErr *protocol.CLIError
	if errors.As(err, &cliErr) {
		return cliErr.Code
	}

	switch {
	case isUsageError(err):
		return "invalid_input"
	case errors.Is(err, store.ErrLeaseConflict):
		return "lease_conflict"
	case errors.Is(err, store.ErrThreadNotFound), errors.Is(err, store.ErrMessageNotFound):
		return "not_found"
	case errors.Is(err, store.ErrInvalidInput):
		return "invalid_input"
	case errors.Is(err, store.ErrInvalidState), errors.Is(err, store.ErrNoActiveLease):
		return "invalid_state"
	default:
		return "internal_error"
	}
}

func renderError(stdout, stderr io.Writer, jsonOutput bool, err error) {
	message := errorMessage(err)
	if jsonOutput {
		_ = protocol.WriteJSON(stdout, protocol.Error{
			OK: false,
			Error: protocol.ErrorPayload{
				Code:    errorCodeForError(err),
				Message: message,
			},
		})
		return
	}

	_, _ = fmt.Fprintln(stderr, message)
}

func errorMessage(err error) string {
	var cliErr *protocol.CLIError
	if errors.As(err, &cliErr) {
		return cliErr.Message
	}
	return err.Error()
}

func hasJSONFlag(args []string) bool {
	for _, arg := range args {
		if arg == "--json" {
			return true
		}
		if strings.HasPrefix(arg, "--json=") {
			return !strings.HasSuffix(arg, "=false")
		}
	}
	return false
}

func isUsageError(err error) bool {
	message := err.Error()
	return strings.HasPrefix(message, "required flag(s)") ||
		strings.HasPrefix(message, "unknown flag:") ||
		strings.HasPrefix(message, "unknown command ") ||
		strings.Contains(message, " accepts ") ||
		strings.Contains(message, "invalid argument ")
}
