package config

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/creasty/defaults"
	"github.com/go-playground/validator/v10"

	sdkconfig "github.com/tidbcloud/aisupport/packages/sdk-go/infra/config"
)

const (
	AgentProviderCodexCLI       = "codex_cli"
	AgentProviderCodexAppServer = "codex_app_server"
	MessageSourceLark           = "lark"
	MessageSourceApplication    = "application"
)

type RootConfig struct {
	Log      sdkconfig.Loggers  `yaml:"log"`
	Secret   SecretConfig       `yaml:"secret"`
	Database sdkconfig.Database `yaml:"database"`

	RPCServer        RPCServerConfig        `yaml:"rpcServer"`
	RPCGatewayServer RPCGatewayServerConfig `yaml:"rpcGatewayServer"`
	Gateway          GatewayConfig          `yaml:"gateway"`
	AgentRuntime     AgentRuntimeConfig     `yaml:"agentRuntime"`
	TaskService      TaskServiceConfig      `yaml:"taskService"`
	Clients          ClientsConfig          `yaml:"clients" validate:"required"`
}

type SecretConfig struct {
	sdkconfig.Secret `yaml:",inline"`

	RootConfigSecretName string `yaml:"rootConfigSecretName"`
}

type RPCServerConfig struct {
	Address string `yaml:"address" default:":9090" validate:"required"`
}

type RPCGatewayServerConfig struct {
	Address    string `yaml:"address" default:":8080" validate:"required"`
	RPCAddress string `yaml:"rpcAddress" default:":9090" validate:"required"`
}

type GatewayConfig struct {
	EventListeners map[string]*GatewayEventListenerConfig `yaml:"eventListeners" validate:"required,min=1,dive,keys,required,endkeys,required"`
}

type GatewayEventListenerConfig struct {
	Sources  []MessageSourceConfig `yaml:"sources" validate:"required,min=1,dive"`
	Endpoint string                `yaml:"endpoint" validate:"required,url"`
	Timeout  time.Duration         `yaml:"timeout" default:"5s" validate:"gt=0"`
}

type MessageSourceConfig struct {
	Type       string `yaml:"type" validate:"required,oneof=lark application"`
	InstanceID string `yaml:"instanceID" validate:"required"`
}

type CodexCLIConfig struct {
	Binary         string        `yaml:"binary" default:"codex" validate:"required"`
	Home           string        `yaml:"home" default:"~" validate:"required"`
	CodexHome      string        `yaml:"codexHome" default:"~/.codex" validate:"required"`
	Timeout        time.Duration `yaml:"timeout" default:"10m" validate:"gt=0"`
	Model          string        `yaml:"model"`
	SandboxMode    string        `yaml:"sandboxMode" default:"workspace-write" validate:"required"`
	ApprovalPolicy string        `yaml:"approvalPolicy" default:"on-request" validate:"required"`
}

type CodexAppServerConfig struct {
	Endpoint       string        `yaml:"endpoint" validate:"required"`
	Token          string        `yaml:"token"`
	Timeout        time.Duration `yaml:"timeout" default:"10m" validate:"gt=0"`
	Model          string        `yaml:"model"`
	SandboxMode    string        `yaml:"sandboxMode" default:"workspace-write" validate:"required"`
	ApprovalPolicy string        `yaml:"approvalPolicy" default:"on-request" validate:"required"`
}

type AgentProviderConfig struct {
	Provider       string                `yaml:"provider" validate:"required,oneof=codex_cli codex_app_server"`
	CodexCLI       *CodexCLIConfig       `yaml:"codexCLI,omitempty" validate:"required_if=Provider codex_cli"`
	CodexAppServer *CodexAppServerConfig `yaml:"codexAppServer,omitempty" validate:"required_if=Provider codex_app_server"`
	Sidecar        *AgentSidecarConfig   `yaml:"sidecar,omitempty"`
}

type AgentSidecarConfig struct {
	Endpoint string        `yaml:"endpoint" validate:"required"`
	Timeout  time.Duration `yaml:"timeout" default:"10s" validate:"gt=0"`
}

type AgentRuntimeConfig struct {
	DefaultAgent string                         `yaml:"defaultAgent" default:"codex" validate:"required"`
	Agents       map[string]AgentProviderConfig `yaml:"agents" validate:"required,min=1,dive,keys,required,endkeys"`
}

type TaskServiceConfig struct {
	MaxConcurrency int `yaml:"maxConcurrency" default:"2" validate:"min=1"`
}

type ClientsConfig struct {
	Core CoreClientConfig `yaml:"core" validate:"required"`
}

type CoreClientConfig struct {
	Address string `yaml:"address" validate:"required"`
}

func Default() RootConfig {
	conf := RootConfig{}
	defaults.MustSet(&conf)
	return conf
}

func (c *RootConfig) Normalize() {
	for _, listener := range c.Gateway.EventListeners {
		defaults.MustSet(listener)
	}
	for name, provider := range c.AgentRuntime.Agents {
		if provider.CodexCLI != nil {
			defaults.MustSet(provider.CodexCLI)
			provider.CodexCLI.Home = expandHome(provider.CodexCLI.Home)
			provider.CodexCLI.CodexHome = expandHome(provider.CodexCLI.CodexHome)
		}
		if provider.CodexAppServer != nil {
			defaults.MustSet(provider.CodexAppServer)
			provider.CodexAppServer.Endpoint = expandEndpoint(provider.CodexAppServer.Endpoint)
		}
		if provider.Sidecar != nil {
			defaults.MustSet(provider.Sidecar)
		}
		c.AgentRuntime.Agents[name] = provider
	}
}

func (c *RootConfig) Validate() error {
	validate := validator.New(validator.WithRequiredStructEnabled())
	if err := validate.Struct(c); err != nil {
		return err
	}
	for name, listener := range c.Gateway.EventListeners {
		if !strings.HasPrefix(listener.Endpoint, "http://") && !strings.HasPrefix(listener.Endpoint, "https://") {
			return fmt.Errorf("gateway event listener %q endpoint must use http:// or https://", name)
		}
	}
	for name, provider := range c.AgentRuntime.Agents {
		if provider.CodexAppServer == nil {
			continue
		}
		endpoint := provider.CodexAppServer.Endpoint
		switch {
		case strings.HasPrefix(endpoint, "unix://"):
			if strings.TrimPrefix(endpoint, "unix://") == "" {
				return fmt.Errorf("agent provider %q codexAppServer endpoint requires a unix socket path", name)
			}
		case strings.HasPrefix(endpoint, "ws://"):
			if provider.CodexAppServer.Token == "" {
				return fmt.Errorf("agent provider %q codexAppServer token is required for ws:// endpoint", name)
			}
		default:
			return fmt.Errorf("agent provider %q codexAppServer endpoint must use unix:// or ws://", name)
		}
	}
	if _, ok := c.AgentRuntime.Agents[c.AgentRuntime.DefaultAgent]; !ok {
		return fmt.Errorf("agent runtime default agent %q is not registered", c.AgentRuntime.DefaultAgent)
	}
	return nil
}

func expandHome(path string) string {
	if path == "~" {
		home, _ := os.UserHomeDir()
		return home
	}
	if !strings.HasPrefix(path, "~/") {
		return path
	}
	home, _ := os.UserHomeDir()
	return filepath.Join(home, path[2:])
}

func expandEndpoint(endpoint string) string {
	if !strings.HasPrefix(endpoint, "unix://") {
		return endpoint
	}
	return "unix://" + expandHome(strings.TrimPrefix(endpoint, "unix://"))
}
