//go:build e2e

package testkit

import (
	"encoding/json"
	"os"
	"strings"
	"sync"
	"testing"
)

// Profile selects how scenario E2E resolves backend URL and accounts.
type Profile string

const (
	// ProfileEmbedded starts cmd/agentteam-e2e-server locally (default).
	ProfileEmbedded Profile = "embedded"
	// ProfileRemote targets an existing AgentTeam deployment via AGENTTEAM_E2E_BASE_URL.
	ProfileRemote Profile = "remote"
)

// UserCredentials holds a pre-provisioned API user for remote runs.
type UserCredentials struct {
	AccessToken string
	UserID      string
}

// Config is the process-wide scenario E2E configuration loaded once from env/files.
type Config struct {
	Profile     Profile
	BaseURL     string
	WorkspaceID string
	UserA       UserCredentials
	UserB       UserCredentials
}

type accountsFile struct {
	BaseURL     string            `json:"baseURL"`
	WorkspaceID string            `json:"workspaceId"`
	UserA       *accountsFileUser `json:"userA"`
	UserB       *accountsFileUser `json:"userB"`
}

type accountsFileUser struct {
	AccessToken string `json:"accessToken"`
	UserID      string `json:"userId"`
}

var (
	configOnce sync.Once
	configVal  Config
)

// LoadConfig returns the loaded scenario configuration for the current test process.
func LoadConfig(t *testing.T) Config {
	t.Helper()
	configOnce.Do(func() {
		configVal = loadConfigFromEnv()
		if path := strings.TrimSpace(os.Getenv("AGENTTEAM_E2E_ACCOUNTS_FILE")); path != "" {
			mergeAccountsFile(&configVal, path)
		}
	})
	t.Logf("e2e profile=%s baseURL=%s workspaceId=%s userA.token=%t userB.token=%t",
		configVal.Profile,
		configVal.BaseURL,
		workspaceIDLogValue(configVal.WorkspaceID),
		configVal.UserA.AccessToken != "",
		configVal.UserB.AccessToken != "",
	)
	return configVal
}

func workspaceIDLogValue(id string) string {
	if id == "" {
		return "<list>"
	}
	return id
}

func loadConfigFromEnv() Config {
	cfg := Config{
		Profile:     profileFromEnv(),
		BaseURL:     strings.TrimSpace(os.Getenv("AGENTTEAM_E2E_BASE_URL")),
		WorkspaceID: strings.TrimSpace(os.Getenv("AGENTTEAM_E2E_WORKSPACE_ID")),
		UserA: UserCredentials{
			AccessToken: firstNonEmptyEnv(
				"AGENTTEAM_E2E_ACCESS_TOKEN",
				"AGENTTEAM_E2E_USER_A_ACCESS_TOKEN",
				"AGENTTEAM_E2E_USER_A_TOKEN",
			),
			UserID: firstNonEmptyEnv(
				"AGENTTEAM_E2E_USER_A_USER_ID",
				"AGENTTEAM_E2E_USER_A_ID",
			),
		},
		UserB: UserCredentials{
			AccessToken: firstNonEmptyEnv(
				"AGENTTEAM_E2E_USER_B_ACCESS_TOKEN",
				"AGENTTEAM_E2E_USER_B_TOKEN",
			),
			UserID: firstNonEmptyEnv(
				"AGENTTEAM_E2E_USER_B_USER_ID",
				"AGENTTEAM_E2E_USER_B_ID",
			),
		},
	}

	if cfg.BaseURL != "" {
		cfg.Profile = ProfileRemote
	}
	if cfg.Profile == "" {
		cfg.Profile = ProfileEmbedded
	}
	return cfg
}

func profileFromEnv() Profile {
	switch strings.ToLower(strings.TrimSpace(os.Getenv("AGENTTEAM_E2E_PROFILE"))) {
	case "remote", "staging":
		return ProfileRemote
	case "embedded", "local":
		return ProfileEmbedded
	default:
		return ""
	}
}

func firstNonEmptyEnv(names ...string) string {
	for _, name := range names {
		if value := strings.TrimSpace(os.Getenv(name)); value != "" {
			return value
		}
	}
	return ""
}

func mergeAccountsFile(cfg *Config, path string) {
	raw, err := os.ReadFile(path)
	if err != nil {
		panic("read AGENTTEAM_E2E_ACCOUNTS_FILE: " + err.Error())
	}
	var file accountsFile
	if err := json.Unmarshal(raw, &file); err != nil {
		panic("parse AGENTTEAM_E2E_ACCOUNTS_FILE: " + err.Error())
	}
	if trimmed := strings.TrimSpace(file.BaseURL); trimmed != "" {
		cfg.BaseURL = trimmed
		cfg.Profile = ProfileRemote
	}
	if trimmed := strings.TrimSpace(file.WorkspaceID); trimmed != "" {
		cfg.WorkspaceID = trimmed
	}
	if file.UserA != nil {
		cfg.UserA = mergeUserCredentials(cfg.UserA, *file.UserA)
	}
	if file.UserB != nil {
		cfg.UserB = mergeUserCredentials(cfg.UserB, *file.UserB)
	}
}

func mergeUserCredentials(base UserCredentials, file accountsFileUser) UserCredentials {
	if trimmed := strings.TrimSpace(file.AccessToken); trimmed != "" {
		base.AccessToken = trimmed
	}
	if trimmed := strings.TrimSpace(file.UserID); trimmed != "" {
		base.UserID = trimmed
	}
	return base
}

// AllowsRegistration reports whether the profile can create fresh users via Register API.
func AllowsRegistration(cfg Config) bool {
	return cfg.Profile == ProfileEmbedded && cfg.UserA.AccessToken == ""
}
