package term

import (
	"io"

	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
)

type NextTerminal struct {
	SshClient  *ssh.Client
	SshSession *ssh.Session
	StdinPipe  io.WriteCloser
	SftpClient *sftp.Client
	Recorder   *Recorder
	NextWriter *NextWriter
}

func NewNextTerminal(ip string, port int, username, password, privateKey, passphrase string, rows, cols int, recording string) (*NextTerminal, error) {

	sshClient, err := NewSshClient(ip, port, username, password, privateKey, passphrase)
	if err != nil {
		return nil, err
	}

	sshSession, err := sshClient.NewSession()
	if err != nil {
		return nil, err
	}
	//defer sshSession.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}

	if err := sshSession.RequestPty("xterm-256color", rows, cols, modes); err != nil {
		return nil, err
	}

	var nextWriter NextWriter
	sshSession.Stdout = &nextWriter
	sshSession.Stderr = &nextWriter

	stdinPipe, err := sshSession.StdinPipe()
	if err != nil {
		return nil, err
	}

	if err := sshSession.Shell(); err != nil {
		return nil, err
	}

	var recorder *Recorder
	if recording != "" {
		recorder, err = CreateRecording(recording, rows, cols)
		if err != nil {
			return nil, err
		}
	}

	terminal := NextTerminal{
		SshClient:  sshClient,
		SshSession: sshSession,
		Recorder:   recorder,
		StdinPipe:  stdinPipe,
		NextWriter: &nextWriter,
	}

	return &terminal, nil
}

func (ret *NextTerminal) Write(p []byte) (int, error) {
	return ret.StdinPipe.Write(p)
}

func (ret *NextTerminal) Read() ([]byte, int, error) {
	bytes, n, err := ret.NextWriter.Read()
	if err != nil {
		return nil, 0, err
	}
	if ret.Recorder != nil && n > 0 {
		_ = ret.Recorder.WriteData(string(bytes))
	}
	return bytes, n, nil
}

func (ret *NextTerminal) Close() error {
	if ret.SshSession != nil {
		return ret.SshSession.Close()
	}

	if ret.SshClient != nil {
		return ret.SshClient.Close()
	}

	if ret.Recorder != nil {
		return ret.Close()
	}

	return nil
}

func (ret *NextTerminal) WindowChange(h int, w int) error {
	return ret.SshSession.WindowChange(h, w)
}
