package testenv

import (
	"context"
	"io"
	"testing"

	"github.com/testcontainers/testcontainers-go"
)

// Platform aggregates a fully booted Galaxy stack: shared Docker
// network, Postgres, Redis, mailpit, backend and gateway. Tests use
// this struct to access HTTP and authenticated-edge endpoints, mailpit and backend
// admin without touching testcontainers directly.
type Platform struct {
	Network  string
	Postgres *Postgres
	Redis    *Redis
	Mailpit  *Mailpit
	Backend  *BackendContainer
	Gateway  *GatewayContainer
}

// BootstrapOptions tunes platform-level knobs that flow into backend
// or gateway configuration. The zero value is valid and produces a
// stack with sensible defaults for happy-path scenarios.
type BootstrapOptions struct {
	BackendExtra map[string]string
	GatewayExtra map[string]string
}

// Bootstrap builds three Docker images (backend, gateway, optionally
// the engine in the caller), spins up Postgres, Redis, mailpit, then
// boots backend and gateway connected to those services. It registers
// t.Cleanup hooks for every component, so callers do not own
// teardown.
//
// The function calls RequireDocker and skips the test gracefully if
// the daemon is unreachable, so every scenario can start with a
// single Bootstrap call.
func Bootstrap(t *testing.T, opts BootstrapOptions) *Platform {
	t.Helper()
	RequireDocker(t)

	net := StartNetwork(t)
	pg := StartPostgres(t, net.Name)
	redis := StartRedis(t, net.Name)
	mp := StartMailpit(t, net.Name)
	geoip := SyntheticGeoIPDB(t)

	backend := StartBackend(t, BackendOptions{
		NetworkAlias:  "backend",
		NetworkName:   net.Name,
		PostgresDSN:   pg.NetworkDSN,
		MailpitHost:   "mailpit",
		MailpitPort:   1025,
		GeoIPHostPath: geoip,
		Extra:         opts.BackendExtra,
	})
	gateway := StartGateway(t, GatewayOptions{
		NetworkAlias:   "gateway",
		NetworkName:    net.Name,
		BackendHTTPURL: "http://backend:8080",
		BackendGRPCURL: "backend:8081",
		RedisAddr:      "redis:6379",
		Extra:          opts.GatewayExtra,
	})

	plat := &Platform{
		Network:  net.Name,
		Postgres: pg,
		Redis:    redis,
		Mailpit:  mp,
		Backend:  backend,
		Gateway:  gateway,
	}
	t.Cleanup(func() {
		if !t.Failed() {
			return
		}
		dumpLogs(t, "backend", backend.Container)
		dumpLogs(t, "gateway", gateway.Container)
	})
	return plat
}

// dumpLogs writes the container's stdout/stderr to test output. Used
// only on failure to surface backend / gateway diagnostics.
func dumpLogs(t *testing.T, name string, c testcontainers.Container) {
	t.Helper()
	if c == nil {
		return
	}
	rc, err := c.Logs(context.Background())
	if err != nil {
		t.Logf("%s logs unavailable: %v", name, err)
		return
	}
	defer rc.Close()
	body, _ := io.ReadAll(rc)
	t.Logf("--- %s container logs ---\n%s", name, string(body))
}
