// Package proxy is a wrapper around proxyguard that integrates it with eduvpn-common settings
// - leaves out some options not applicable to the common integration, e.g. fwmark
// - integrates with eduvpn-common's logger
// - integrates eduvpn-common's user agent
package proxy

import (
	"context"
	"fmt"
	"log/slog"

	"codeberg.org/eduVPN/proxyguard"

	"codeberg.org/eduVPN/eduvpn-common/i18n/err"
	"codeberg.org/eduVPN/eduvpn-common/internal/httpwrap"
)

// Logger is defined here such that we can update the proxyguard logger
type Logger struct{}

// Logf logs a message with parameters
func (l *Logger) Logf(msg string, params ...any) {
	slog.Info("Proxyguard log", "msg", fmt.Sprintf(msg, params...))
}

// Log logs a message
func (l *Logger) Log(msg string) {
	slog.Info("Proxyguard log", "msg", msg)
}

// Proxy is the ProxyGuard client with a channel used for restarting
type Proxy struct {
	proxyguard.Client
	resChan chan struct{}
}

// NewProxyguard sets up proxyguard for proxied WireGuard connections
func NewProxyguard(ctx context.Context, lp int, tcpsp int, peer string, setupSocket func(fd int)) (*Proxy, error) {
	proxyguard.UpdateLogger(&Logger{})
	proxy := Proxy{
		Client: proxyguard.Client{
			Peer:          peer,
			ListenPort:    lp,
			TCPSourcePort: tcpsp,
			SetupSocket:   setupSocket,
			UserAgent:     httpwrap.UserAgent,
		},
		resChan: make(chan struct{}),
	}
	_, err := proxy.Setup(ctx)
	if err != nil {
		return nil, i18nerr.WrapInternal(err, "The ProxyGuard DNS could not be resolved")
	}

	return &proxy, nil
}

// Tunnel tunnels the ProxyGuard connection. `wglisten` is the WireGuard listen port
func (p *Proxy) Tunnel(ctx context.Context, wglisten int) error {
	errChan := make(chan error, 1)
	gctx, cancel := context.WithCancel(ctx)
	go func() {
		err := p.Client.Tunnel(gctx, wglisten)
		if err != nil {
			err = i18nerr.WrapInternal(err, "The VPN proxy exited")
		}
		errChan <- err
	}()

	select {
	case err := <-errChan:
		cancel()
		return err
	case <-p.resChan:
		cancel()
		<-errChan
		return p.Tunnel(ctx, wglisten)
	}
}

// Restart restarts the existing ProxyGuard process, for e.g. roaming
func (p *Proxy) Restart() {
	p.resChan <- struct{}{}
}
