package main

import (
	"os"
	"path/filepath"
	"slices"
	"testing"

	appservice "github.com/pingcap-inc/nexuscrm-app-packs/appservices/sfcrm"
)

func TestProcessCapabilitiesPublishGoogleLoginStateProtocol(t *testing.T) {
	if !slices.Contains(processCapabilities(), appservice.GoogleLoginStateCapabilityID) {
		t.Fatalf("process capabilities do not publish %q", appservice.GoogleLoginStateCapabilityID)
	}
}

func TestProcessCapabilitiesPublishMetadataAdminRead(t *testing.T) {
	if !slices.Contains(processCapabilities(), appservice.MetadataAdminReadCapabilityID) {
		t.Fatalf("process capabilities do not publish %q", appservice.MetadataAdminReadCapabilityID)
	}
}

func TestProcessCapabilitiesPublishRecordDefaults(t *testing.T) {
	if !slices.Contains(processCapabilities(), appservice.RecordDefaultsCapabilityID) {
		t.Fatalf("process capabilities do not publish %q", appservice.RecordDefaultsCapabilityID)
	}
}

func TestLoadConfigRequiresCompleteExplicitProcessAndRuntimeFacts(t *testing.T) {
	directory := t.TempDir()
	tokenFile := filepath.Join(directory, "workload-token")
	if err := os.WriteFile(tokenFile, []byte("workload-secret\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	values := map[string]string{
		"SFCRM_HTTP_ADDRESS": "127.0.0.1:8080", "SFCRM_RUNTIME_TARGET": "runtime:9090",
		"SFCRM_APP_INSTALLATION_ID": "installation-1", "SFCRM_WORKLOAD_TOKEN_FILE": tokenFile,
		"SFCRM_PUBLIC_ORIGIN": "https://crm.example.test", "SFCRM_ORGANIZATION_ID": "org-1",
		"SFCRM_SESSION_CONTROL_ORIGINS": "https://crm.example.test",
		"SFCRM_RUNTIME_CA_FILE":         "/run/identity/ca.pem", "SFCRM_RUNTIME_SERVER_NAME": "runtime.test",
		"SFCRM_REQUEST_TIMEOUT": "5s", "SFCRM_RUNTIME_ATTEMPT_TIMEOUT": "2s", "SFCRM_RUNTIME_STARTUP_TIMEOUT": "10s", "SFCRM_SHUTDOWN_TIMEOUT": "10s",
		"SFCRM_READ_HEADER_TIMEOUT": "5s", "SFCRM_IDLE_TIMEOUT": "60s", "SFCRM_GOOGLE_TIMEOUT": "10s", "SFCRM_MAX_REQUEST_BODY_BYTES": "27262976",
	}
	lookup := func(name string) (string, bool) { value, ok := values[name]; return value, ok }
	config, err := loadConfig(lookup)
	if err != nil {
		t.Fatal(err)
	}
	if config.runtime.WorkloadToken != "workload-secret" || config.runtime.CAFile != "/run/identity/ca.pem" ||
		config.runtime.AttemptTimeout.String() != "2s" ||
		config.requestTimeout.String() != "5s" || config.maxRequestBytes != 27262976 ||
		!config.requireFederatedLoginState {
		t.Fatalf("config=%#v", config)
	}
	delete(values, "SFCRM_REQUEST_TIMEOUT")
	if _, err := loadConfig(lookup); err == nil {
		t.Fatal("missing explicit timeout succeeded")
	}
}

func TestLoadConfigSupportsExplicitFederatedLoginStateRolloutFence(t *testing.T) {
	directory := t.TempDir()
	tokenFile := filepath.Join(directory, "workload-token")
	if err := os.WriteFile(tokenFile, []byte("workload-secret\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	values := map[string]string{
		"SFCRM_HTTP_ADDRESS": "127.0.0.1:8080", "SFCRM_RUNTIME_TARGET": "runtime:9090",
		"SFCRM_APP_INSTALLATION_ID": "installation-1", "SFCRM_WORKLOAD_TOKEN_FILE": tokenFile,
		"SFCRM_PUBLIC_ORIGIN": "https://crm.example.test", "SFCRM_ORGANIZATION_ID": "org-1",
		"SFCRM_SESSION_CONTROL_ORIGINS": "https://crm.example.test",
		"SFCRM_REQUEST_TIMEOUT":         "5s", "SFCRM_RUNTIME_ATTEMPT_TIMEOUT": "2s", "SFCRM_RUNTIME_STARTUP_TIMEOUT": "10s", "SFCRM_SHUTDOWN_TIMEOUT": "10s",
		"SFCRM_READ_HEADER_TIMEOUT": "5s", "SFCRM_IDLE_TIMEOUT": "60s", "SFCRM_GOOGLE_TIMEOUT": "10s", "SFCRM_MAX_REQUEST_BODY_BYTES": "27262976",
		"SFCRM_REQUIRE_FEDERATED_LOGIN_STATE": "false",
	}
	lookup := func(name string) (string, bool) { value, ok := values[name]; return value, ok }
	config, err := loadConfig(lookup)
	if err != nil {
		t.Fatal(err)
	}
	if config.requireFederatedLoginState {
		t.Fatalf("compatibility rollout fence remained strict: %#v", config)
	}

	values["SFCRM_REQUIRE_FEDERATED_LOGIN_STATE"] = "sometimes"
	if _, err := loadConfig(lookup); err == nil {
		t.Fatal("invalid federated login-state rollout fence succeeded")
	}
}

func TestLoadConfigRejectsBodyLimitBelowRecordFileContract(t *testing.T) {
	directory := t.TempDir()
	tokenFile := filepath.Join(directory, "workload-token")
	if err := os.WriteFile(tokenFile, []byte("workload-secret"), 0o600); err != nil {
		t.Fatal(err)
	}
	values := map[string]string{
		"SFCRM_HTTP_ADDRESS": "127.0.0.1:8080", "SFCRM_RUNTIME_TARGET": "runtime:9090",
		"SFCRM_APP_INSTALLATION_ID": "installation-1", "SFCRM_WORKLOAD_TOKEN_FILE": tokenFile,
		"SFCRM_REQUEST_TIMEOUT": "5s", "SFCRM_RUNTIME_ATTEMPT_TIMEOUT": "2s",
		"SFCRM_RUNTIME_STARTUP_TIMEOUT": "10s", "SFCRM_SHUTDOWN_TIMEOUT": "10s",
		"SFCRM_READ_HEADER_TIMEOUT": "5s", "SFCRM_IDLE_TIMEOUT": "60s",
		"SFCRM_MAX_REQUEST_BODY_BYTES": "26214400",
	}
	lookup := func(name string) (string, bool) { value, ok := values[name]; return value, ok }
	if _, err := loadConfig(lookup); err == nil {
		t.Fatal("body limit without multipart framing budget succeeded")
	}
}

func TestLoadConfigRejectsMultiLineWorkloadToken(t *testing.T) {
	tokenFile := filepath.Join(t.TempDir(), "workload-token")
	if err := os.WriteFile(tokenFile, []byte("one\ntwo\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	values := map[string]string{
		"SFCRM_HTTP_ADDRESS": "127.0.0.1:8080", "SFCRM_RUNTIME_TARGET": "runtime:9090",
		"SFCRM_APP_INSTALLATION_ID": "installation-1", "SFCRM_WORKLOAD_TOKEN_FILE": tokenFile,
	}
	lookup := func(name string) (string, bool) { value, ok := values[name]; return value, ok }
	if _, err := loadConfig(lookup); err == nil {
		t.Fatal("multi-line workload token succeeded")
	}
}

func TestLoadConfigRejectsWorldReadableWorkloadToken(t *testing.T) {
	tokenFile := filepath.Join(t.TempDir(), "workload-token")
	if err := os.WriteFile(tokenFile, []byte("workload-secret"), 0o644); err != nil {
		t.Fatal(err)
	}
	values := map[string]string{
		"SFCRM_HTTP_ADDRESS": "127.0.0.1:8080", "SFCRM_RUNTIME_TARGET": "runtime:9090",
		"SFCRM_APP_INSTALLATION_ID": "installation-1", "SFCRM_WORKLOAD_TOKEN_FILE": tokenFile,
	}
	lookup := func(name string) (string, bool) { value, ok := values[name]; return value, ok }
	if _, err := loadConfig(lookup); err == nil {
		t.Fatal("world-readable workload token succeeded")
	}
}
