//go:build integration

package integrationtest

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/testcontainers/testcontainers-go"
	"github.com/testcontainers/testcontainers-go/modules/minio"
	"github.com/testcontainers/testcontainers-go/modules/postgres"
	"github.com/testcontainers/testcontainers-go/wait"
)

type infra struct {
	pgContainer    testcontainers.Container
	minioContainer testcontainers.Container
	miniredis      *miniredis.Miniredis
	dbURL          string
	s3Endpoint     string
	s3AccessKey    string
	s3SecretKey    string
	redisAddr      string
}

func sharedInfra(ctx context.Context, env Env) (*infra, error) {
	return startInfra(ctx, env)
}

func startInfra(ctx context.Context, env Env) (*infra, error) {
	out := &infra{
		dbURL:       env.DBURL,
		s3Endpoint:  env.S3Endpoint,
		s3AccessKey: env.S3AccessKey,
		s3SecretKey: env.S3SecretKey,
		redisAddr:   env.RedisAddr,
	}

	if out.dbURL == "" {
		pg, err := postgres.Run(ctx,
			"postgres:16-alpine",
			postgres.WithDatabase("ultidb_test"),
			postgres.WithUsername("ulti"),
			postgres.WithPassword("test"),
			testcontainers.WithWaitStrategy(
				wait.ForLog("database system is ready to accept connections").
					WithOccurrence(2).
					WithStartupTimeout(60*time.Second),
			),
		)
		if err != nil {
			return nil, fmt.Errorf("postgres container: %w", err)
		}
		out.pgContainer = pg
		connStr, err := pg.ConnectionString(ctx, "sslmode=disable")
		if err != nil {
			return nil, fmt.Errorf("postgres connection string: %w", err)
		}
		out.dbURL = connStr
	}

	if out.redisAddr == "" {
		mr, err := miniredis.Run()
		if err != nil {
			return nil, fmt.Errorf("miniredis: %w", err)
		}
		out.miniredis = mr
		out.redisAddr = mr.Addr()
	}

	if out.s3Endpoint == "" {
		m, err := minio.Run(ctx,
			"minio/minio:latest",
			minio.WithUsername(env.S3AccessKey),
			minio.WithPassword(env.S3SecretKey),
		)
		if err != nil {
			return nil, fmt.Errorf("minio container: %w", err)
		}
		out.minioContainer = m
		host, err := m.ConnectionString(ctx)
		if err != nil {
			return nil, fmt.Errorf("minio endpoint: %w", err)
		}
		out.s3Endpoint = host
	}

	if out.s3AccessKey == "" {
		out.s3AccessKey = "ultiadmin"
	}
	if out.s3SecretKey == "" {
		out.s3SecretKey = "changeme123"
	}

	return out, nil
}

func (i *infra) Close(ctx context.Context) {
	if i == nil {
		return
	}
	if i.miniredis != nil {
		i.miniredis.Close()
	}
	if i.pgContainer != nil {
		_ = i.pgContainer.Terminate(ctx)
	}
	if i.minioContainer != nil {
		_ = i.minioContainer.Terminate(ctx)
	}
}

func normalizeEndpoint(endpoint string) string {
	return strings.TrimPrefix(strings.TrimPrefix(endpoint, "http://"), "https://")
}
