package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/statping/statping/handlers"
	"github.com/statping/statping/source"
	"github.com/statping/statping/types/configs"
	"github.com/statping/statping/types/core"
	"github.com/statping/statping/types/services"
	"github.com/statping/statping/utils"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var (
	importAll *bool
)

func assetsCli() error {
	dir := utils.Directory
	if err := utils.InitLogs(); err != nil {
		return err
	}
	if err := source.Assets(); err != nil {
		return err
	}
	if err := source.CreateAllAssets(dir); err != nil {
		return err
	}
	return nil
}

func systemctlCli(dir string, uninstall bool, port int64) error {
	location := "/etc/systemd/system/statping.service"

	if uninstall {
		fmt.Println("systemctl stop statping")
		if _, _, err := utils.Command("systemctl", "stop", "statping"); err != nil {
			log.Errorln(err)
		}
		fmt.Println("systemctl disable statping")
		if _, _, err := utils.Command("systemctl", "disable", "statping"); err != nil {
			log.Errorln(err)
		}
		fmt.Println("Deleting systemctl: ", location)
		if err := utils.DeleteFile(location); err != nil {
			log.Errorln(err)
		}
		return nil
	}
	if ok := utils.FolderExists(dir); !ok {
		return errors.New("directory does not exist: " + dir)
	}

	binPath, err := os.Executable()
	if err != nil {
		return err
	}

	config := []byte(`[Unit]
Description=Statping Server
After=network.target
After=systemd-user-sessions.service
After=network-online.target

[Service]
Type=simple
Restart=always
Environment="STATPING_DIR=` + dir + `"
Environment="ALLOW_REPORTS=true"
ExecStart=` + binPath + ` --port=` + utils.ToString(port) + `
WorkingDirectory=` + dir + `

[Install]
WantedBy=multi-user.target"
`)
	fmt.Println("Saving systemctl service to: ", location)
	fmt.Printf("Using directory %s for Statping data\n", dir)
	fmt.Printf("Running on port %d\n", port)
	fmt.Printf("\n\n%s\n\n", string(config))
	if err := utils.SaveFile(location, config); err != nil {
		return err
	}
	fmt.Println("systemctl daemon-reload")
	if _, _, err := utils.Command("systemctl", "daemon-reload"); err != nil {
		return err
	}
	fmt.Println("systemctl enable statping")
	if _, _, err := utils.Command("systemctl", "enable", "statping.service"); err != nil {
		return err
	}
	fmt.Println("systemctl start statping")
	if _, _, err := utils.Command("systemctl", "start", "statping"); err != nil {
		return err
	}
	fmt.Println("Statping was will auto start on reboots")
	fmt.Println("systemctl service: ", location)

	return nil
}

func exportCli(args []string) error {
	filename := filepath.Join(utils.Directory, time.Now().Format("01-02-2006-1504")+".json")
	if len(args) == 1 {
		filename = fmt.Sprintf("%s/%s", utils.Directory, args)
	}
	var data *handlers.ExportData
	if err := utils.InitLogs(); err != nil {
		return err
	}
	if err := source.Assets(); err != nil {
		return err
	}
	config, err := configs.LoadConfigs(configFile)
	if err != nil {
		return err
	}
	if err = configs.ConnectConfigs(config, false); err != nil {
		return err
	}
	if _, err := services.SelectAllServices(false); err != nil {
		return err
	}
	if data, err = handlers.ExportSettings(); err != nil {
		return fmt.Errorf("could not export settings: %v", err.Error())
	}
	if err = utils.SaveFile(filename, data.JSON()); err != nil {
		return fmt.Errorf("could not write file statping-export.json: %v", err.Error())
	}
	log.Infoln("Statping export file saved to ", filename)
	return nil
}

func sassCli() error {
	if err := utils.InitLogs(); err != nil {
		return err
	}
	if err := source.Assets(); err != nil {
		return err
	}
	if err := source.CompileSASS(); err != nil {
		return err
	}
	return nil
}

func resetCli() error {
	d := utils.Directory
	fmt.Println("Statping directory: ", d)
	assets := d + "/assets"
	if utils.FolderExists(assets) {
		fmt.Printf("Deleting %s folder.\n", assets)
		if err := utils.DeleteDirectory(assets); err != nil {
			return err
		}
	} else {
		fmt.Printf("Assets folder does not exist %s\n", assets)
	}

	logDir := d + "/logs"
	if utils.FolderExists(logDir) {
		fmt.Printf("Deleting %s directory.\n", logDir)
		if err := utils.DeleteDirectory(logDir); err != nil {
			return err
		}
	} else {
		fmt.Printf("Logs folder does not exist %s\n", logDir)
	}

	c := d + "/config.yml"
	if utils.FileExists(c) {
		fmt.Printf("Deleting %s file.\n", c)
		if err := utils.DeleteFile(c); err != nil {
			return err
		}
	} else {
		fmt.Printf("Config file does not exist %s\n", c)
	}

	dbFile := d + "/statping.db"
	if utils.FileExists(dbFile) {
		fmt.Printf("Backuping up %s file.\n", dbFile)
		if err := utils.RenameDirectory(dbFile, d+"/statping.db.backup"); err != nil {
			return err
		}
	} else {
		fmt.Printf("Statping SQL Database file does not exist %s\n", dbFile)
	}

	fmt.Println("Statping has been reset")
	return nil
}

func envCli() error {
	fmt.Println("Statping Configuration")
	fmt.Printf("Process ID:          %d\n", os.Getpid())
	fmt.Printf("Running as user id:  %d\n", os.Getuid())
	fmt.Printf("Running as group id: %d\n", os.Getgid())
	fmt.Printf("Statping Directory:  %s\n", utils.Directory)
	for k, v := range utils.Params.AllSettings() {
		fmt.Printf("%s=%v\n", strings.ToUpper(k), v)
	}
	return nil
}

func onceCli() error {
	if err := utils.InitLogs(); err != nil {
		return err
	}
	if err := source.Assets(); err != nil {
		return err
	}
	log.Infoln("Running 1 time and saving to database...")
	if err := runOnce(); err != nil {
		return err
	}
	//core.CloseDB()
	fmt.Println("Check is complete.")
	return nil
}

func importCli(args []string) error {
	var err error
	var data []byte
	if len(args) < 1 {
		return errors.New("invalid command arguments")
	}
	if data, err = ioutil.ReadFile(args[0]); err != nil {
		return err
	}
	var exportData handlers.ExportData
	if err = json.Unmarshal(data, &exportData); err != nil {
		return err
	}
	log.Printf("=== %s ===\n", exportData.Core.Name)
	if exportData.Config != nil {
		log.Printf("Configs:     %s\n", exportData.Config.DbConn)
		if exportData.Config.DbUser != "" {
			log.Printf("   - Host:   %s\n", exportData.Config.DbHost)
			log.Printf("   - User:   %s\n", exportData.Config.DbUser)
		}
	}
	if len(exportData.Services) > 0 {
		log.Printf("Services:   %d\n", len(exportData.Services))
	}
	if len(exportData.Checkins) > 0 {
		log.Printf("Checkins:   %d\n", len(exportData.Checkins))
	}
	if len(exportData.Groups) > 0 {
		log.Printf("Groups:     %d\n", len(exportData.Groups))
	}
	if len(exportData.Messages) > 0 {
		log.Printf("Messages:   %d\n", len(exportData.Messages))
	}
	if len(exportData.Incidents) > 0 {
		log.Printf("Incidents:  %d\n", len(exportData.Incidents))
	}
	if len(exportData.Users) > 0 {
		log.Printf("Users:      %d\n", len(exportData.Users))
	}

	if exportData.Config != nil {
		if ask("Create config.yml file from Configs?") {
			log.Printf("Database Host:   	%s\n", exportData.Config.DbHost)
			log.Printf("Database Port:   	%d\n", exportData.Config.DbPort)
			log.Printf("Database User:   	%s\n", exportData.Config.DbUser)
			log.Printf("Database Password:   %s\n", exportData.Config.DbPass)
			if err := exportData.Config.Save(utils.Directory); err != nil {
				return err
			}
		}
	}

	config, err := configs.LoadConfigs(configFile)
	if err != nil {
		return err
	}
	if err = configs.ConnectConfigs(config, false); err != nil {
		return err
	}
	if ask("Create database rows and sample data?") {
		if err := config.ResetCore(); err != nil {
			return err
		}
	}
	if ask("Import Core settings?") {
		c := exportData.Core
		if err := c.Update(); err != nil {
			log.Errorln(err)
		}
	}
	for _, s := range exportData.Groups {
		if ask(fmt.Sprintf("Import Group '%s'?", s.Name)) {
			s.Id = 0
			if err := s.Create(); err != nil {
				log.Errorln(err)
			}
		}
	}
	for _, s := range exportData.Services {
		if ask(fmt.Sprintf("Import Service '%s'?", s.Name)) {
			s.Id = 0
			if err := s.Create(); err != nil {
				log.Errorln(err)
			}
		}
	}
	for _, s := range exportData.Checkins {
		if ask(fmt.Sprintf("Import Checkin '%s'?", s.Name)) {
			s.Id = 0
			if err := s.Create(); err != nil {
				log.Errorln(err)
			}
		}
	}
	for _, s := range exportData.Messages {
		if ask(fmt.Sprintf("Import Message '%s'?", s.Title)) {
			s.Id = 0
			if err := s.Create(); err != nil {
				log.Errorln(err)
			}
		}
	}
	for _, s := range exportData.Users {
		if ask(fmt.Sprintf("Import User '%s'?", s.Username)) {
			s.Id = 0
			if err := s.Create(); err != nil {
				log.Errorln(err)
			}
		}
	}
	log.Infof("Import complete")
	return nil
}

func ask(format string) bool {

	fmt.Printf(fmt.Sprintf(format + " [y/N]: "))
	reader := bufio.NewReader(os.Stdin)
	text, _ := reader.ReadString('\n')
	text = strings.Replace(text, "\n", "", -1)
	return strings.ToLower(text) == "y"
}

func updateDisplay() error {
	gitCurrent, err := checkGithubUpdates()
	if err != nil {
		return errors.Wrap(err, "Issue connecting to https://github.com/statping/statping")
	}
	if gitCurrent.TagName == "" {
		return nil
	}
	if len(gitCurrent.TagName) < 2 {
		return nil
	}
	if VERSION != gitCurrent.TagName[1:] {
		fmt.Printf("New Update %v Available!\n", gitCurrent.TagName[1:])
		fmt.Printf("Update Command:\n")
		fmt.Printf("curl -o- -L https://statping.com/install.sh | bash\n\n")
	}
	return nil
}

// runOnce will initialize the Statping application and check each service 1 time, will not run HTTP server
func runOnce() error {
	config, err := configs.LoadConfigs(configFile)
	if err != nil {
		return errors.Wrap(err, "config.yml file not found")
	}
	err = configs.ConnectConfigs(config, false)
	if err != nil {
		return errors.Wrap(err, "issue connecting to database")
	}
	c, err := core.Select()
	if err != nil {
		return errors.Wrap(err, "core database was not found or setup")
	}

	core.App = c

	_, err = services.SelectAllServices(true)
	if err != nil {
		return errors.Wrap(err, "could not select all services")
	}
	for _, srv := range services.Services() {
		srv.CheckService(true)
	}
	return nil
}

func checkGithubUpdates() (githubResponse, error) {
	url := "https://api.github.com/repos/statping/statping/releases/latest"
	contents, _, err := utils.HttpRequest(url, "GET", nil, nil, nil, time.Duration(2*time.Second), true, nil)
	if err != nil {
		return githubResponse{}, err
	}
	var gitResp githubResponse
	err = json.Unmarshal(contents, &gitResp)
	return gitResp, err
}

type githubResponse struct {
	URL             string      `json:"url"`
	AssetsURL       string      `json:"assets_url"`
	UploadURL       string      `json:"upload_url"`
	HTMLURL         string      `json:"html_url"`
	ID              int         `json:"id"`
	NodeID          string      `json:"node_id"`
	TagName         string      `json:"tag_name"`
	TargetCommitish string      `json:"target_commitish"`
	Name            string      `json:"name"`
	Draft           bool        `json:"draft"`
	Author          gitAuthor   `json:"author"`
	Prerelease      bool        `json:"prerelease"`
	CreatedAt       time.Time   `json:"created_at"`
	PublishedAt     time.Time   `json:"published_at"`
	Assets          []gitAssets `json:"assets"`
	TarballURL      string      `json:"tarball_url"`
	ZipballURL      string      `json:"zipball_url"`
	Body            string      `json:"body"`
}

type gitAuthor struct {
	Login             string `json:"login"`
	ID                int    `json:"id"`
	NodeID            string `json:"node_id"`
	AvatarURL         string `json:"avatar_url"`
	GravatarID        string `json:"gravatar_id"`
	URL               string `json:"url"`
	HTMLURL           string `json:"html_url"`
	FollowersURL      string `json:"followers_url"`
	FollowingURL      string `json:"following_url"`
	GistsURL          string `json:"gists_url"`
	StarredURL        string `json:"starred_url"`
	SubscriptionsURL  string `json:"subscriptions_url"`
	OrganizationsURL  string `json:"organizations_url"`
	ReposURL          string `json:"repos_url"`
	EventsURL         string `json:"events_url"`
	ReceivedEventsURL string `json:"received_events_url"`
	Type              string `json:"type"`
	SiteAdmin         bool   `json:"site_admin"`
}

type gitAssets struct {
	URL                string      `json:"url"`
	ID                 int         `json:"id"`
	NodeID             string      `json:"node_id"`
	Name               string      `json:"name"`
	Label              string      `json:"label"`
	Uploader           gitUploader `json:"uploader"`
	ContentType        string      `json:"content_type"`
	State              string      `json:"state"`
	Size               int         `json:"size"`
	DownloadCount      int         `json:"download_count"`
	CreatedAt          time.Time   `json:"created_at"`
	UpdatedAt          time.Time   `json:"updated_at"`
	BrowserDownloadURL string      `json:"browser_download_url"`
}

type gitUploader struct {
	Login             string `json:"login"`
	ID                int    `json:"id"`
	NodeID            string `json:"node_id"`
	AvatarURL         string `json:"avatar_url"`
	GravatarID        string `json:"gravatar_id"`
	URL               string `json:"url"`
	HTMLURL           string `json:"html_url"`
	FollowersURL      string `json:"followers_url"`
	FollowingURL      string `json:"following_url"`
	GistsURL          string `json:"gists_url"`
	StarredURL        string `json:"starred_url"`
	SubscriptionsURL  string `json:"subscriptions_url"`
	OrganizationsURL  string `json:"organizations_url"`
	ReposURL          string `json:"repos_url"`
	EventsURL         string `json:"events_url"`
	ReceivedEventsURL string `json:"received_events_url"`
	Type              string `json:"type"`
	SiteAdmin         bool   `json:"site_admin"`
}

// ExportChartsJs renders the charts for the index page

//type ExportData struct {
//	Config    *configs.DbConfig   `json:"config"`
//	Core      *core.Core          `json:"core"`
//	Services  []services.Service  `json:"services"`
//	Messages  []*messages.Message `json:"messages"`
//	Checkins  []*checkins.Checkin `json:"checkins"`
//	Users     []*users.User       `json:"users"`
//	Groups    []*groups.Group     `json:"groups"`
//	Notifiers []core.AllNotifiers `json:"notifiers"`
//}

// ExportSettings will export a JSON file containing all of the settings below:
// - Core
// - Notifiers
// - Checkins
// - Users
// - Services
// - Groups
// - Messages
//func ExportSettings() ([]byte, error) {
//	c, err := core.Select()
//	if err != nil {
//		return nil, err
//	}
//	var srvs []services.Service
//	for _, s := range services.AllInOrder() {
//		s.Failures = nil
//		srvs = append(srvs, s)
//	}
//
//	cfg, err := configs.LoadConfigs(configFile)
//	if err != nil {
//		return nil, err
//	}
//
//	data := ExportData{
//		Config:    cfg,
//		Core:      c,
//		Notifiers: core.App.Notifications,
//		Checkins:  checkins.All(),
//		Users:     users.All(),
//		Services:  srvs,
//		Groups:    groups.All(),
//		Messages:  messages.All(),
//	}
//	export, err := json.Marshal(data)
//	return export, err
//}

// ExportIndexHTML returns the HTML of the index page as a string
//func ExportIndexHTML() []byte {
//	source.Assets()
//	core.CoreApp.Connect(core.CoreApp., utils.Directory)
//	core.SelectAllServices(false)
//	core.CoreApp.UseCdn = types.NewNullBool(true)
//	for _, srv := range core.Services() {
//		core.CheckService(srv, true)
//	}
//	w := httptest.NewRecorder()
//	r := httptest.NewRequest("GET", "/", nil)
//	handlers.ExecuteResponse(w, r, "index.gohtml", nil, nil)
//	return w.Body.Bytes()
//}
