package test

import (
	"context"
	_ "embed"
	"fmt"
	"os"
	"path/filepath"

	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/go-errors/errors"
	"github.com/jackc/pgconn"
	"github.com/jackc/pgerrcode"
	"github.com/jackc/pgx/v4"
	"github.com/spf13/afero"
	"github.com/spf13/viper"
	"github.com/supabase/cli/internal/utils"
	cliConfig "github.com/supabase/cli/pkg/config"
)

const (
	ENABLE_PGTAP  = "create extension if not exists pgtap with schema extensions"
	DISABLE_PGTAP = "drop extension if exists pgtap"
)

func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
	// Build test command
	cmd := []string{"pg_prove", "--ext", ".pg", "--ext", ".sql", "-r"}
	for _, fp := range testFiles {
		relPath, err := filepath.Rel(utils.DbTestsDir, fp)
		if err != nil {
			return errors.Errorf("failed to resolve relative path: %w", err)
		}
		cmd = append(cmd, relPath)
	}
	if viper.GetBool("DEBUG") {
		cmd = append(cmd, "--verbose")
	}
	// Mount tests directory into container as working directory
	srcPath, err := filepath.Abs(utils.DbTestsDir)
	if err != nil {
		return errors.Errorf("failed to resolve absolute path: %w", err)
	}
	dstPath := "/tmp"
	binds := []string{fmt.Sprintf("%s:%s:ro", srcPath, dstPath)}
	// Enable pgTAP if not already exists
	alreadyExists := false
	options = append(options, func(cc *pgx.ConnConfig) {
		cc.OnNotice = func(pc *pgconn.PgConn, n *pgconn.Notice) {
			alreadyExists = n.Code == pgerrcode.DuplicateObject
		}
	})
	conn, err := utils.ConnectByConfig(ctx, config, options...)
	if err != nil {
		return err
	}
	defer conn.Close(context.Background())
	if _, err := conn.Exec(ctx, ENABLE_PGTAP); err != nil {
		return errors.Errorf("failed to enable pgTAP: %w", err)
	}
	if !alreadyExists {
		defer func() {
			if _, err := conn.Exec(ctx, DISABLE_PGTAP); err != nil {
				fmt.Fprintln(os.Stderr, "failed to disable pgTAP:", err)
			}
		}()
	}
	// Use custom network when connecting to local database
	// disable selinux via security-opt to allow pg-tap to work properly
	hostConfig := container.HostConfig{Binds: binds, SecurityOpt: []string{"label:disable"}}
	if utils.IsLocalDatabase(config) {
		config.Host = utils.DbAliases[0]
		config.Port = 5432
	} else {
		hostConfig.NetworkMode = network.NetworkHost
	}
	// Run pg_prove on volume mount
	return utils.DockerRunOnceWithConfig(
		ctx,
		container.Config{
			Image: cliConfig.PgProveImage,
			Env: []string{
				"PGHOST=" + config.Host,
				fmt.Sprintf("PGPORT=%d", config.Port),
				"PGUSER=" + config.User,
				"PGPASSWORD=" + config.Password,
				"PGDATABASE=" + config.Database,
			},
			Cmd:        cmd,
			WorkingDir: dstPath,
		},
		hostConfig,
		network.NetworkingConfig{},
		"",
		os.Stdout,
		os.Stderr,
	)
}
