package core

import (
	"encoding/json"
	uuid "github.com/satori/go.uuid"
	log "github.com/sirupsen/logrus"
	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/storage"
	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Migrate all changes, current struct fields change
func MigrateInitialStructChange() error {
	clients, err := readClients()
	if err != nil {
		return err
	}

	s, err := deserialize("server.json")
	if err != nil {
		return err
	}

	for _, client := range clients {
		switch v := client["allowedIPs"].(type) {
		case []interface{}:
			log.Infof("client %s has been already migrated", client["id"])
			continue
		default:
			log.Infof("unexpected type %T, mus be migrated", v)
		}

		c := &model.Client{}
		c.Id = client["id"].(string)
		c.Name = client["name"].(string)
		c.Email = client["email"].(string)
		c.Enable = client["enable"].(bool)
		c.AllowedIPs = make([]string, 0)
		for _, address := range strings.Split(client["allowedIPs"].(string), ",") {
			if util.IsValidCidr(strings.TrimSpace(address)) {
				c.AllowedIPs = append(c.AllowedIPs, strings.TrimSpace(address))
			}
		}
		c.Address = make([]string, 0)
		for _, address := range strings.Split(client["address"].(string), ",") {
			if util.IsValidCidr(strings.TrimSpace(address)) {
				c.Address = append(c.Address, strings.TrimSpace(address))
			}
		}
		c.PrivateKey = client["privateKey"].(string)
		c.PublicKey = client["publicKey"].(string)
		created, err := time.Parse(time.RFC3339, client["created"].(string))
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to parse time")
			continue
		}
		c.Created = created
		updated, err := time.Parse(time.RFC3339, client["updated"].(string))
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to parse time")
			continue
		}
		c.Updated = updated

		err = storage.Serialize(c.Id, c)
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to Serialize client")
		}
	}

	switch v := s["address"].(type) {
	case []interface{}:
		log.Info("server has been already migrated")
		return nil
	default:
		log.Infof("unexpected type %T, mus be migrated", v)
	}

	server := &model.Server{}

	server.Address = make([]string, 0)
	for _, address := range strings.Split(s["address"].(string), ",") {
		if util.IsValidCidr(strings.TrimSpace(address)) {
			server.Address = append(server.Address, strings.TrimSpace(address))
		}
	}
	server.ListenPort = int(s["listenPort"].(float64))
	server.PrivateKey = s["privateKey"].(string)
	server.PublicKey = s["publicKey"].(string)
	//server.PresharedKey = s["presharedKey"].(string)
	server.Endpoint = s["endpoint"].(string)
	server.PersistentKeepalive = int(s["persistentKeepalive"].(float64))
	server.Dns = make([]string, 0)
	for _, address := range strings.Split(s["dns"].(string), ",") {
		if util.IsValidIp(strings.TrimSpace(address)) {
			server.Dns = append(server.Dns, strings.TrimSpace(address))
		}
	}
	if val, ok := s["preUp"]; ok {
		server.PreUp = val.(string)
	}
	if val, ok := s["postUp"]; ok {
		server.PostUp = val.(string)
	}
	if val, ok := s["preDown"]; ok {
		server.PreDown = val.(string)
	}
	if val, ok := s["postDown"]; ok {
		server.PostDown = val.(string)
	}
	created, err := time.Parse(time.RFC3339, s["created"].(string))
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Errorf("failed to parse time")
	}
	server.Created = created
	updated, err := time.Parse(time.RFC3339, s["updated"].(string))
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Errorf("failed to parse time")
	}
	server.Updated = updated

	err = storage.Serialize("server.json", server)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Errorf("failed to Serialize server")
	}

	return nil
}

// Migrate presharedKey issue #23
func MigratePresharedKey() error {
	clients, err := readClients()
	if err != nil {
		return err
	}

	s, err := deserialize("server.json")
	if err != nil {
		return err
	}

	for _, client := range clients {
		if _, ok := client["presharedKey"]; ok {
			log.Infof("client %s has been already migrated for preshared key", client["id"])
			continue
		}

		c := &model.Client{}
		c.Id = client["id"].(string)
		c.Name = client["name"].(string)
		c.Email = client["email"].(string)
		c.Enable = client["enable"].(bool)
		if val, ok := client["ignorePersistentKeepalive"]; ok {
			c.IgnorePersistentKeepalive = val.(bool)
		} else {
			c.IgnorePersistentKeepalive = false
		}
		c.PresharedKey = s["presharedKey"].(string)
		c.AllowedIPs = make([]string, 0)
		for _, address := range client["allowedIPs"].([]interface{}) {
			c.AllowedIPs = append(c.AllowedIPs, address.(string))
		}
		c.Address = make([]string, 0)
		for _, address := range client["address"].([]interface{}) {
			c.Address = append(c.Address, address.(string))
		}
		c.PrivateKey = client["privateKey"].(string)
		c.PublicKey = client["publicKey"].(string)
		created, err := time.Parse(time.RFC3339, client["created"].(string))
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to parse time")
			continue
		}
		c.Created = created
		updated, err := time.Parse(time.RFC3339, client["updated"].(string))
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to parse time")
			continue
		}
		c.Updated = updated

		err = storage.Serialize(c.Id, c)
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to Serialize client")
		}
	}

	if _, ok := s["presharedKey"]; ok {
		server := &model.Server{}

		server.Address = make([]string, 0)
		server.Address = make([]string, 0)
		for _, address := range s["address"].([]interface{}) {
			server.Address = append(server.Address, address.(string))
		}
		server.ListenPort = int(s["listenPort"].(float64))
		server.PrivateKey = s["privateKey"].(string)
		server.PublicKey = s["publicKey"].(string)
		server.Endpoint = s["endpoint"].(string)
		server.PersistentKeepalive = int(s["persistentKeepalive"].(float64))
		server.Dns = make([]string, 0)
		for _, address := range s["dns"].([]interface{}) {
			server.Dns = append(server.Dns, address.(string))
		}
		if val, ok := s["preUp"]; ok {
			server.PreUp = val.(string)
		}
		if val, ok := s["postUp"]; ok {
			server.PostUp = val.(string)
		}
		if val, ok := s["preDown"]; ok {
			server.PreDown = val.(string)
		}
		if val, ok := s["postDown"]; ok {
			server.PostDown = val.(string)
		}
		created, err := time.Parse(time.RFC3339, s["created"].(string))
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to parse time")
		}
		server.Created = created
		updated, err := time.Parse(time.RFC3339, s["updated"].(string))
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to parse time")
		}
		server.Updated = updated

		err = storage.Serialize("server.json", server)
		if err != nil {
			log.WithFields(log.Fields{
				"err": err,
			}).Errorf("failed to Serialize server")
		}

	}

	return nil
}

func readClients() ([]map[string]interface{}, error) {
	clients := make([]map[string]interface{}, 0)

	files, err := ioutil.ReadDir(filepath.Join(os.Getenv("WG_CONF_DIR")))
	if err != nil {
		return nil, err
	}

	for _, f := range files {
		// clients file name is an uuid
		_, err := uuid.FromString(f.Name())
		if err == nil {
			c, err := deserialize(f.Name())
			if err != nil {
				log.WithFields(log.Fields{
					"err":  err,
					"path": f.Name(),
				}).Error("failed to deserialize client")
			} else {
				clients = append(clients, c)
			}
		}
	}

	return clients, nil
}

func deserialize(id string) (map[string]interface{}, error) {
	path := filepath.Join(os.Getenv("WG_CONF_DIR"), id)

	data, err := util.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var d map[string]interface{}
	err = json.Unmarshal(data, &d)
	if err != nil {
		return nil, err
	}

	return d, nil
}