package main

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

	"github.com/curusarn/resh/internal/cfg"
	"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 developement bool

func main() {
	config, errCfg := cfg.New()
	logger, _ := logger.New("daemon", config.LogLevel, developement)
	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("Deamon starting ...",
		"version", version,
		"commit", commit,
	)

	// xdgCacheHome := d.getEnvOrPanic("__RESH_XDG_CACHE_HOME")
	// xdgDataHome := d.getEnvOrPanic("__RESH_XDG_DATA_HOME")

	// TODO: rethink PID file and logs location
	homeDir, err := os.UserHomeDir()
	if err != nil {
		sugar.Fatalw("Could not get user home dir", zap.Error(err))
	}
	PIDFile := filepath.Join(homeDir, ".resh/resh.pid")
	reshHistoryPath := filepath.Join(homeDir, ".resh_history.json")
	bashHistoryPath := filepath.Join(homeDir, ".bash_history")
	zshHistoryPath := filepath.Join(homeDir, ".zsh_history")

	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.Warn("Pidfile exists")
		// 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 pidfile",
			"error", err,
			"PIDFile", PIDFile,
		)
	}
	server := Server{
		sugar:           sugar,
		config:          config,
		reshHistoryPath: reshHistoryPath,
		bashHistoryPath: bashHistoryPath,
		zshHistoryPath:  zshHistoryPath,
	}
	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) getEnvOrPanic(envVar string) string {
	val, found := os.LookupEnv(envVar)
	if !found {
		d.sugar.Fatalw("Required env variable is not set",
			"variableName", envVar,
		)
	}
	return val
}

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("Succesfully 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
}
