package portforward

import (
	"fmt"
	"github.com/cirruslabs/orchard/internal/proxy"
	"github.com/cirruslabs/orchard/pkg/client"
	"github.com/spf13/cobra"
	"net"
)

var wait uint16

func newPortForwardVMCommand() *cobra.Command {
	command := &cobra.Command{
		Use:   "vm VM_NAME [LOCAL_PORT]:REMOTE_PORT",
		Short: "Forward TCP port to the VM",
		Args:  cobra.ExactArgs(2),
		RunE:  runPortForwardVMCommand,
	}

	command.Flags().Uint16VarP(&wait, "wait", "t", 60,
		"Amount of seconds to wait for the VM to start running if it's not running already")

	return command
}

func runPortForwardVMCommand(cmd *cobra.Command, args []string) (err error) {
	name := args[0]
	portSpecRaw := args[1]

	portSpec, err := NewPortSpec(portSpecRaw)
	if err != nil {
		return err
	}

	client, err := client.New()
	if err != nil {
		return err
	}

	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", portSpec.LocalPort))
	if err != nil {
		return err
	}
	defer func() {
		if listenerErr := listener.Close(); listenerErr != nil && err == nil {
			err = listenerErr
		}
	}()

	fmt.Printf("forwarding %s -> %s:%d...\n", listener.Addr(), name, portSpec.RemotePort)

	errCh := make(chan error, 1)

	go func() {
		for {
			conn, err := listener.Accept()
			if err != nil {
				errCh <- err

				return
			}

			go func() {
				defer conn.Close()

				wsConn, err := client.VMs().PortForward(cmd.Context(), name, portSpec.RemotePort, wait)
				if err != nil {
					fmt.Printf("failed to forward port: %v\n", err)

					return
				}
				defer wsConn.Close()

				if err := proxy.Connections(wsConn, conn); err != nil {
					fmt.Printf("failed to forward port: %v\n", err)
				}
			}()
		}
	}()

	select {
	case <-cmd.Context().Done():
		return cmd.Context().Err()
	case err := <-errCh:
		return err
	}
}
