package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/curusarn/resh/internal/cfg"
	"github.com/curusarn/resh/internal/datadir"
	"github.com/curusarn/resh/internal/device"
	"github.com/curusarn/resh/internal/httpclient"
	"github.com/curusarn/resh/internal/logger"
	"go.uber.org/zap"
)

// info passed during build
var version string
var commit string
var development string

func main() {
	config, errCfg := cfg.New()
	logger, err := logger.New("daemon", config.LogLevel, development)
	if err != nil {
		fmt.Printf("Error while creating logger: %v", err)
	}
	defer logger.Sync() // flushes buffer, if any
	if errCfg != nil {
		logger.Error("Error while getting configuration", zap.Error(errCfg))
	}
	sugar := logger.Sugar()
	d := daemon{sugar: sugar}
	sugar.Infow("Daemon starting ...",
		"version", version,
		"commit", commit,
	)
	dataDir, err := datadir.MakePath()
	if err != nil {
		sugar.Fatalw("Could not get user data directory", zap.Error(err))
	}
	homeDir, err := os.UserHomeDir()
	if err != nil {
		sugar.Fatalw("Could not get user home directory", zap.Error(err))
	}
	// TODO: These paths should be probably defined in a package
	pidFile := filepath.Join(dataDir, "daemon.pid")
	reshHistoryPath := filepath.Join(dataDir, "history.reshjson")
	bashHistoryPath := filepath.Join(homeDir, ".bash_history")
	zshHistoryPath := filepath.Join(homeDir, ".zsh_history")
	deviceID, err := device.GetID(dataDir)
	if err != nil {
		sugar.Fatalw("Could not get resh device ID", zap.Error(err))
	}
	deviceName, err := device.GetName(dataDir)
	if err != nil {
		sugar.Fatalw("Could not get resh device name", zap.Error(err))
	}

	sugar = sugar.With(zap.Int("daemonPID", os.Getpid()))

	res, err := d.isDaemonRunning(config.Port)
	if err != nil {
		sugar.Errorw("Error while checking daemon status - it's probably not running",
			"error", err)
	}
	if res {
		sugar.Errorw("Daemon is already running - exiting!")
		return
	}
	_, err = os.Stat(pidFile)
	if err == nil {
		sugar.Warnw("PID file exists",
			"PIDFile", pidFile)
		// kill daemon
		err = d.killDaemon(pidFile)
		if err != nil {
			sugar.Errorw("Could not kill daemon",
				"error", err,
			)
		}
	}
	err = ioutil.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0644)
	if err != nil {
		sugar.Fatalw("Could not create PID file",
			"error", err,
			"PIDFile", pidFile,
		)
	}
	server := Server{
		sugar:           sugar,
		config:          config,
		reshHistoryPath: reshHistoryPath,
		bashHistoryPath: bashHistoryPath,
		zshHistoryPath:  zshHistoryPath,

		deviceID:   deviceID,
		deviceName: deviceName,
	}
	server.Run()
	sugar.Infow("Removing PID file ...",
		"PIDFile", pidFile,
	)
	err = os.Remove(pidFile)
	if err != nil {
		sugar.Errorw("Could not delete PID file", "error", err)
	}
	sugar.Info("Shutting down ...")
}

type daemon struct {
	sugar *zap.SugaredLogger
}

func (d *daemon) isDaemonRunning(port int) (bool, error) {
	url := "http://localhost:" + strconv.Itoa(port) + "/status"
	client := httpclient.New()
	resp, err := client.Get(url)
	if err != nil {
		return false, err
	}
	defer resp.Body.Close()
	return true, nil
}

func (d *daemon) killDaemon(pidFile string) error {
	dat, err := ioutil.ReadFile(pidFile)
	if err != nil {
		d.sugar.Errorw("Reading PID file failed",
			"PIDFile", pidFile,
			"error", err)
	}
	d.sugar.Infow("Successfully read PID file", "contents", string(dat))
	pid, err := strconv.Atoi(strings.TrimSuffix(string(dat), "\n"))
	if err != nil {
		return fmt.Errorf("could not parse PID file contents: %w", err)
	}
	d.sugar.Infow("Successfully parsed PID", "PID", pid)
	cmd := exec.Command("kill", "-s", "sigint", strconv.Itoa(pid))
	err = cmd.Run()
	if err != nil {
		return fmt.Errorf("kill command finished with error: %w", err)
	}
	return nil
}
