package sshproxy

import (
	"fmt"
	"io"
	"net"
	"sync"
)

type proxyConnection struct {
	internalPort int
	externalPort int
	listener     net.Listener

	// closedChan is used to notify that the proxy connection is closed
	closedChan chan<- *proxyConnection
}

func newProxyConnection(toPort int, closedChan chan *proxyConnection) (*proxyConnection, error) {
	l, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, err
	}

	externalPort := l.Addr().(*net.TCPAddr).Port

	return &proxyConnection{
		internalPort: toPort,
		externalPort: externalPort,
		listener:     l,
		closedChan:   closedChan,
	}, nil
}

func (c *proxyConnection) start() {
	for {
		conn, err := c.listener.Accept()
		if err != nil {
			fmt.Printf("error accepting connection at %v: %v\n", c.listener.Addr(), err)
		}
		go c.forwardConnectionToSSH(conn)
	}
}

func (c *proxyConnection) forwardConnectionToSSH(conn net.Conn) {
	containerConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", c.internalPort))
	if err != nil {
		fmt.Printf("error connecting to container ssh at port %d\n", c.internalPort)
		return
	}
	defer func() {
		c.closedChan <- c
	}()
	defer containerConn.Close()

	var wg sync.WaitGroup
	defer wg.Wait()

	wg.Add(1)
	go func() {
		defer wg.Done()
		defer conn.Close()
		for {
			_, err := io.Copy(conn, containerConn)
			if err != nil {
				fmt.Println("read remote conn err", err)
				break
			}
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		defer conn.Close()
		for {
			_, err := io.Copy(containerConn, conn)
			if err != nil {
				fmt.Println("write remote conn err", err)
				break
			}
		}
	}()
}
