package flags

import (
	"fmt"
	"time"

	"github.com/cenkalti/backoff/v4"
	"github.com/urfave/cli/v2"
)

var (
	commonCategory   = "COMMON"
	metricsCategory  = "METRICS"
	loggingCategory  = "LOGGING"
	driverCategory   = "DRIVER"
	proposerCategory = "PROPOSER"
	proverCategory   = "PROVER"
	txmgrCategory    = "TX_MANAGER"
)

// Required flags used by all client software.
var (
	L1WSEndpoint = &cli.StringFlag{
		Name: "l1.ws",
		Usage: "WebSocket RPC endpoint of an L1 ethereum node " +
			"(omit to use --l1.http with polling; required for prover/proposer)",
		Category: commonCategory,
		EnvVars:  []string{"L1_WS"},
	}
	L1PrivateEndpoint = &cli.StringFlag{
		Name:     "l1.private",
		Usage:    "RPC endpoint of a L1 node with private mempool",
		Category: commonCategory,
		EnvVars:  []string{"L1_PRIVATE"},
	}
	L1HTTPEndpoint = &cli.StringFlag{
		Name:     "l1.http",
		Usage:    "HTTP RPC endpoint of an L1 ethereum node (used as a polling fallback when --l1.ws is not provided)",
		Category: commonCategory,
		EnvVars:  []string{"L1_HTTP"},
	}
	L2WSEndpoint = &cli.StringFlag{
		Name: "l2.ws",
		Usage: "WebSocket RPC endpoint of a L2 taiko-geth execution engine " +
			"(omit to use --l2.http with polling; required for prover/proposer)",
		Category: commonCategory,
		EnvVars:  []string{"L2_WS"},
	}
	L1BeaconEndpoint = &cli.StringFlag{
		Name:     "l1.beacon",
		Usage:    "HTTP RPC endpoint of a L1 beacon node",
		Category: commonCategory,
		EnvVars:  []string{"L1_BEACON"},
	}
	L2HTTPEndpoint = &cli.StringFlag{
		Name: "l2.http",
		Usage: "HTTP RPC endpoint of a L2 taiko-geth execution engine " +
			"(driver: polling fallback when --l2.ws absent)",
		Category: commonCategory,
		EnvVars:  []string{"L2_HTTP"},
	}
	L2AuthEndpoint = &cli.StringFlag{
		Name:     "l2.auth",
		Usage:    "Authenticated HTTP RPC endpoint of a L2 taiko-geth execution engine",
		Required: true,
		Category: commonCategory,
		EnvVars:  []string{"L2_AUTH"},
	}
	JWTSecret = &cli.StringFlag{
		Name:     "jwtSecret",
		Usage:    "Path to a JWT secret to use for authenticated RPC endpoints",
		Required: true,
		Category: commonCategory,
		EnvVars:  []string{"JWT_SECRET"},
	}
	InboxAddress = &cli.StringFlag{
		Name:     "inbox",
		Usage:    "Inbox contract `address`",
		Required: true,
		Category: commonCategory,
		EnvVars:  []string{"INBOX"},
	}
	TaikoAnchorAddress = &cli.StringFlag{
		Name:     "taikoAnchor",
		Usage:    "Taiko anchor contract `address`",
		Required: true,
		Category: commonCategory,
		EnvVars:  []string{"TAIKO_ANCHOR"},
	}

	// Optional flags used by all client software.
	// Logging
	Verbosity = &cli.IntFlag{
		Name:     "verbosity",
		Usage:    "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail",
		Value:    3,
		Category: loggingCategory,
		EnvVars:  []string{"VERBOSITY"},
	}
	LogJSON = &cli.BoolFlag{
		Name:     "log.json",
		Usage:    "Format logs with JSON",
		Category: loggingCategory,
		EnvVars:  []string{"LOG_JSON"},
	}
	// Metrics
	MetricsEnabled = &cli.BoolFlag{
		Name:     "metrics",
		Usage:    "Enable metrics collection and reporting",
		Category: metricsCategory,
		Value:    false,
		EnvVars:  []string{"METRICS"},
	}
	MetricsAddr = &cli.StringFlag{
		Name:     "metrics.addr",
		Usage:    "Metrics reporting server listening address",
		Category: metricsCategory,
		Value:    "0.0.0.0",
		EnvVars:  []string{"METRICS_ADDR"},
	}
	MetricsPort = &cli.IntFlag{
		Name:     "metrics.port",
		Usage:    "Metrics reporting server listening port",
		Category: metricsCategory,
		Value:    6060,
		EnvVars:  []string{"METRICS_PORT"},
	}
	BackOffMaxRetries = &cli.Uint64Flag{
		Name:     "backoff.maxRetries",
		Usage:    "Max retry times when there is an error",
		Category: commonCategory,
		Value:    10,
		EnvVars:  []string{"BACKOFF_MAX_RETRIES"},
	}
	BackOffRetryInterval = &cli.DurationFlag{
		Name:     "backoff.retryInterval",
		Usage:    "Retry interval in seconds when there is an error",
		Category: commonCategory,
		Value:    backoff.DefaultMaxInterval,
		EnvVars:  []string{"BACKOFF_RETRY_INTERVAL"},
	}
	RPCTimeout = &cli.DurationFlag{
		Name:     "rpc.timeout",
		Usage:    "Timeout in seconds for RPC calls",
		Category: commonCategory,
		Value:    12 * time.Second,
		EnvVars:  []string{"RPC_TIMEOUT"},
	}
	TaikoDevnetUnzenTime = &cli.Uint64Flag{
		Name:     "taiko.devnet-unzen-time",
		Usage:    "Override Unzen fork time for Taiko internal devnet; must match taiko-geth's --taiko.devnet-unzen-time",
		Category: commonCategory,
		Value:    0,
		EnvVars:  []string{"TAIKO_DEVNET_UNZEN_TIME"},
	}
)

// CommonFlags All common flags.
var CommonFlags = []cli.Flag{
	// Required
	InboxAddress,
	TaikoAnchorAddress,
	// Optional
	L1WSEndpoint,
	L1HTTPEndpoint,
	Verbosity,
	LogJSON,
	MetricsEnabled,
	MetricsAddr,
	MetricsPort,
	BackOffMaxRetries,
	BackOffRetryInterval,
	RPCTimeout,
	L1PrivateEndpoint,
	TaikoDevnetUnzenTime,
}

// MergeFlags merges the given flag slices.
func MergeFlags(groups ...[]cli.Flag) []cli.Flag {
	var merged []cli.Flag
	for _, group := range groups {
		merged = append(merged, group...)
	}
	return merged
}

// CheckWSEndpointsRequired returns an error if either the L1 or L2 WS endpoint
// flag is empty. The cli library no longer marks --l1.ws / --l2.ws as Required
// (the driver can fall back to HTTP polling), so components that need WS — the
// proposer and prover — enforce it here. component names the caller for the
// error message (e.g. "proposer").
func CheckWSEndpointsRequired(c *cli.Context, component string) error {
	if c.String(L1WSEndpoint.Name) == "" {
		return fmt.Errorf("flag --%s is required for the %s", L1WSEndpoint.Name, component)
	}
	if c.String(L2WSEndpoint.Name) == "" {
		return fmt.Errorf("flag --%s is required for the %s", L2WSEndpoint.Name, component)
	}
	return nil
}
