package main

import (
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"time"

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

func translateRaspiName(targetName string, cfg RaspiConfig) (string, string, string, string) {
	for _, n := range cfg.Raspis {
		if targetName == "" {
			break
		}
		if n.Name == targetName {
			// Found!
			return n.Hostname, n.Port, n.Username, n.Password
		}
	}

	return "", "", "", ""
}

func createSSHClient(targetName string, cfg RaspiConfig) (*ssh.Client, error) {
	hostname, port, username, password := translateRaspiName(targetName, cfg)
	log.Println("Connecting to:", hostname, port)

	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), // OK for internal tools (and me in production)
		Timeout:         3 * time.Second,
	}

	connAddr := net.JoinHostPort(hostname, port)
	return ssh.Dial("tcp", connAddr, config)
}

func runSSHCommand(client *ssh.Client, cmd string) ([]byte, error) {
	session, err := client.NewSession()
	if err != nil {
		return nil, err
	}
	defer session.Close()
	return session.CombinedOutput(cmd)
}

func verifyCred(targetName string, cfg RaspiConfig) bool {
	sshClient, err := createSSHClient(targetName, cfg)

	if err != nil {
		log.Printf("Err occurred %v", err)
		return false
	}
	defer sshClient.Close()

	log.Println("Successfully verified the credentials")
	return true
}

func sftpUploadFile(targetName, localPath, remotePath string, cfg RaspiConfig) bool {
	const clearDirectory string = "rm /opt/raspscreen/media/*"

	sshClient, err := createSSHClient(targetName, cfg)

	fExt := filepath.Ext(localPath)
	remotePath = remotePath + fExt

	if err != nil {
		log.Printf("Failed to init the SSH connection %v", err)
		return false
	}
	defer sshClient.Close()

	sftpClient, err := sftp.NewClient(sshClient)
	if err != nil {
		log.Printf("Failed to start SFTP: %v", err)
		return false
	}
	defer sftpClient.Close()

	localFile, err := os.Open(localPath)
	if err != nil {
		log.Printf("Failed to open local file: %v", err)
		return false
	}
	defer localFile.Close()

	log.Println("Clearing directory")
	_, err = runSSHCommand(sshClient, clearDirectory)

	remoteFile, err := sftpClient.Create(remotePath)
	if err != nil {
		log.Printf("Failed to open remote file handle: %v", err)
		return false
	}

	log.Println("Uploading file")
	bytesCopied, err := io.Copy(remoteFile, localFile)
	if err != nil {
		log.Printf("Failed to copy local file to the remote location: %v", err)
		return false
	}

	log.Printf("Succesfully uploaded %d bytes to %s", bytesCopied, remotePath)
	return true
}

func restartShow(targetName string, targetMode int, cfg RaspiConfig) bool {
	const restartPresentation string = "systemctl --user restart presentation"
	const enablePresentation string = "systemctl --user enable presentation"
	const disablePresentation string = "systemctl --user disable --now presentation"

	const restartVideo string = "systemctl --user restart video"
	const enableVideo string = "systemctl --user enable video"
	const disableVideo string = "systemctl --user disable --now video"

	var err error
	sshClient, err := createSSHClient(targetName, cfg)

	if err != nil {
		log.Printf("Failed to init the SSH connection: %v", err)
		return false
	}
	defer sshClient.Close()

	session, err := sshClient.NewSession()
	if err != nil {
		log.Printf("Failed to create session over the connection: %v", err)
		return false
	}
	defer session.Close()

	switch targetMode {
	case 1:
		log.Println("Configuring application workflow: Presentation")
		_, err = runSSHCommand(sshClient, restartPresentation)
		_, err = runSSHCommand(sshClient, disableVideo)
		_, err = runSSHCommand(sshClient, enablePresentation)
	case 2:
		log.Println("Configuring application workflow: Video")
		_, err = runSSHCommand(sshClient, restartVideo)
		_, err = runSSHCommand(sshClient, disablePresentation)
		_, err = runSSHCommand(sshClient, enableVideo)
	}

	if err != nil {
		log.Printf("Failed to restart the show over SSH: %v", err)
		return false
	}

	log.Println("Done reloading program!")
	return true
}
