//go:build !go1.19

package tunnel

//
// Psiphon not working with go1.19
//
// TODO(https://github.com/ooni/probe/issues/2222)
//

import (
	"context"
	"fmt"
	"net"
	"net/url"
	"path/filepath"
	"time"

	"github.com/ooni/psiphon/tunnel-core/ClientLibrary/clientlib"
)

// mockableStartPsiphon allows us to test for psiphon startup failures.
var mockableStartPsiphon = func(
	ctx context.Context, config []byte, workdir string) (*clientlib.PsiphonTunnel, error) {
	return clientlib.StartTunnel(ctx, config, "", clientlib.Parameters{
		DataRootDirectory: &workdir}, nil, nil)
}

// psiphonTunnel is a psiphon tunnel
type psiphonTunnel struct {
	// bootstrapTime is the bootstrapTime of the bootstrap
	bootstrapTime time.Duration

	// tunnel is the underlying psiphon tunnel
	tunnel *clientlib.PsiphonTunnel
}

// psiphonMakeWorkingDir creates the working directory
func psiphonMakeWorkingDir(config *Config) (string, error) {
	workdir := filepath.Join(config.TunnelDir, config.Name)
	if err := config.mkdirAll(workdir, 0700); err != nil {
		return "", err
	}
	return workdir, nil
}

// psiphonStart starts the psiphon tunnel.
func psiphonStart(ctx context.Context, config *Config) (Tunnel, DebugInfo, error) {
	debugInfo := DebugInfo{
		LogFilePath: "",
		Name:        "psiphon",
		Version:     "",
	}
	select {
	case <-ctx.Done():
		return nil, debugInfo, ctx.Err() // simplifies unit testing this code
	default:
	}
	if config.TunnelDir == "" {
		return nil, debugInfo, ErrEmptyTunnelDir
	}
	configJSON, err := config.Session.FetchPsiphonConfig(ctx)
	if err != nil {
		return nil, debugInfo, err
	}
	workdir, err := psiphonMakeWorkingDir(config)
	if err != nil {
		return nil, debugInfo, err
	}
	start := time.Now()
	tunnel, err := mockableStartPsiphon(ctx, configJSON, workdir)
	if err != nil {
		return nil, debugInfo, err
	}
	stop := time.Now()
	return &psiphonTunnel{
		tunnel:        tunnel,
		bootstrapTime: stop.Sub(start),
	}, debugInfo, nil
}

// Stop is an idempotent method that shuts down the tunnel
func (t *psiphonTunnel) Stop() {
	t.tunnel.Stop()
}

// SOCKS5ProxyURL returns the SOCKS5 proxy URL.
func (t *psiphonTunnel) SOCKS5ProxyURL() *url.URL {
	return &url.URL{
		Scheme: "socks5",
		Host: net.JoinHostPort(
			"127.0.0.1", fmt.Sprintf("%d", t.tunnel.SOCKSProxyPort)),
	}
}

// BootstrapTime returns the bootstrap time
func (t *psiphonTunnel) BootstrapTime() time.Duration {
	return t.bootstrapTime
}
