package config

import (
	"encoding/json"
	"fmt"
	"os"
	"time"
)

type Config struct {
	Server  ServerConfig  `json:"server"`
	Gateway GatewayConfig `json:"gateway"`
	Auth    AuthConfig    `json:"auth"`
}

type ServerConfig struct {
	HTTPListen             string `json:"httpListen"`
	ReadTimeoutSeconds     int    `json:"readTimeoutSeconds"`
	WriteTimeoutSeconds    int    `json:"writeTimeoutSeconds"`
	IdleTimeoutSeconds     int    `json:"idleTimeoutSeconds"`
	ShutdownTimeoutSeconds int    `json:"shutdownTimeoutSeconds"`
}

type GatewayConfig struct {
	MaxPayloadBytes       int `json:"maxPayloadBytes"`
	WriteWaitSeconds      int `json:"writeWaitSeconds"`
	PongWaitSeconds       int `json:"pongWaitSeconds"`
	PingIntervalSeconds   int `json:"pingIntervalSeconds"`
	MaxLatestStateEntries int `json:"maxLatestStateEntries"`
}

type AuthConfig struct {
	ProducerTokens          []string `json:"producerTokens"`
	ConsumerTokens          []string `json:"consumerTokens"`
	ControllerTokens        []string `json:"controllerTokens"`
	AllowAnonymousConsumers bool     `json:"allowAnonymousConsumers"`
}

func Load(path string) (Config, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return Config{}, fmt.Errorf("read config: %w", err)
	}

	var cfg Config
	if err := json.Unmarshal(data, &cfg); err != nil {
		return Config{}, fmt.Errorf("parse config: %w", err)
	}

	cfg.applyDefaults()
	if err := cfg.validate(); err != nil {
		return Config{}, err
	}
	return cfg, nil
}

func (c *Config) applyDefaults() {
	if c.Server.HTTPListen == "" {
		c.Server.HTTPListen = ":8080"
	}
	if c.Server.ReadTimeoutSeconds <= 0 {
		c.Server.ReadTimeoutSeconds = 15
	}
	if c.Server.WriteTimeoutSeconds <= 0 {
		c.Server.WriteTimeoutSeconds = 15
	}
	if c.Server.IdleTimeoutSeconds <= 0 {
		c.Server.IdleTimeoutSeconds = 60
	}
	if c.Server.ShutdownTimeoutSeconds <= 0 {
		c.Server.ShutdownTimeoutSeconds = 10
	}
	if c.Gateway.MaxPayloadBytes <= 0 {
		c.Gateway.MaxPayloadBytes = 64 * 1024
	}
	if c.Gateway.WriteWaitSeconds <= 0 {
		c.Gateway.WriteWaitSeconds = 10
	}
	if c.Gateway.PongWaitSeconds <= 0 {
		c.Gateway.PongWaitSeconds = 60
	}
	if c.Gateway.PingIntervalSeconds <= 0 {
		c.Gateway.PingIntervalSeconds = 25
	}
	if c.Gateway.MaxLatestStateEntries <= 0 {
		c.Gateway.MaxLatestStateEntries = 10000
	}
}

func (c Config) validate() error {
	if c.Gateway.PingInterval() >= c.Gateway.PongWait() {
		return fmt.Errorf("gateway.pingIntervalSeconds must be smaller than gateway.pongWaitSeconds")
	}
	return nil
}

func (c ServerConfig) ReadTimeout() time.Duration {
	return time.Duration(c.ReadTimeoutSeconds) * time.Second
}

func (c ServerConfig) WriteTimeout() time.Duration {
	return time.Duration(c.WriteTimeoutSeconds) * time.Second
}

func (c ServerConfig) IdleTimeout() time.Duration {
	return time.Duration(c.IdleTimeoutSeconds) * time.Second
}

func (c ServerConfig) ShutdownTimeout() time.Duration {
	return time.Duration(c.ShutdownTimeoutSeconds) * time.Second
}

func (c GatewayConfig) WriteWait() time.Duration {
	return time.Duration(c.WriteWaitSeconds) * time.Second
}

func (c GatewayConfig) PongWait() time.Duration {
	return time.Duration(c.PongWaitSeconds) * time.Second
}

func (c GatewayConfig) PingInterval() time.Duration {
	return time.Duration(c.PingIntervalSeconds) * time.Second
}
