package term

import (
	"bufio"
	"io"
	"log"
	"strconv"
	"strings"

	"github.com/gofiber/contrib/websocket"
	"rul.sh/vaulterm/server/lib"
)

func NewSSHWebsocketSession(c *websocket.Conn, client *lib.SSHClient) ([]byte, error) {
	if err := client.Connect(); err != nil {
		log.Printf("error connecting to SSH: %v", err)
		return nil, err
	}
	defer client.Close()

	shell, err := client.StartPtyShell()
	if err != nil {
		log.Printf("error starting SSH shell: %v", err)
		return nil, err
	}

	session := shell.Session
	defer session.Close()

	sessionCapture, sessionCaptureWriter := io.Pipe()
	defer sessionCapture.Close()
	sessionLog := []byte{}

	// Capture SSH session output
	go func() {
		reader := bufio.NewReader(sessionCapture)
		for {
			b, err := reader.ReadBytes('\n')
			if err != nil {
				break
			}
			sessionLog = append(sessionLog, b...)
		}
	}()

	// Pass SSH stdout to WebSocket
	go func() {
		buf := make([]byte, 1024)
		for {
			n, err := shell.Stdout.Read(buf)
			if err != nil {
				break
			}
			sessionCaptureWriter.Write(buf[:n])
			if err := c.WriteMessage(websocket.BinaryMessage, buf[:n]); err != nil {
				break
			}
		}
	}()

	// Pass SSH stderr to WebSocket
	go func() {
		buf := make([]byte, 1024)
		for {
			n, err := shell.Stderr.Read(buf)
			if err != nil {
				break
			}
			sessionCaptureWriter.Write(buf[:n])
			if err := c.WriteMessage(websocket.BinaryMessage, buf[:n]); err != nil {
				break
			}
		}
	}()

	// Handle user input
	go func() {
		defer session.Close()

		for {
			_, msg, err := c.ReadMessage()
			if err != nil {
				break
			}

			if strings.HasPrefix(string(msg), "\x01") {
				parts := strings.Split(string(msg[1:]), ",")
				if len(parts) == 2 {
					width, _ := strconv.Atoi(parts[0])
					height, _ := strconv.Atoi(parts[1])
					session.WindowChange(height, width)
				}
				continue
			}

			shell.Stdin.Write(msg)
		}
	}()

	// Wait for the SSH session to close
	if err := session.Wait(); err != nil {
		log.Printf("SSH session ended with error: %v", err)
		return sessionLog, err
	}

	return sessionLog, nil
}