package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/UpCloudLtd/upcloud-go-api/upcloud"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
	"gopkg.in/yaml.v3"
)

func NewStoreConfig(dataPath string) (*StoreConfig, error) {
	// Create config structure
	storeConfig := &StoreConfig{}

	// Open config file
	file, err := os.Open(dataPath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// Init new YAML decode
	d := yaml.NewDecoder(file)

	// Start YAML decoding from file
	if err := d.Decode(&storeConfig); err != nil {
		return nil, err
	}

	return storeConfig, nil
}

func loadStoreConfig(dataCfgPath string) (*StoreConfig, error) {
	// datacfg := &DataConfig{}
	storecfg, err := NewStoreConfig(dataCfgPath)
	if err != nil {
		log.Fatal(err)
	  return storecfg, err
	}
	return storecfg, nil
}

func getStorageDevices(srv ServerConfig) []request.CreateServerStorageDevice {
  var storageDevices []request.CreateServerStorageDevice
	for _,storage := range srv.StorageDevices {
		storageDevices = append(storageDevices,request.CreateServerStorageDevice{
			Action : storage.Action,
			Title : storage.Title,
			Storage : storage.Storage,
			Size  : storage.Size,
			Tier  : storage.Tier,
		})
	}
	return storageDevices
}
func deleteStorage(s *service.Service, uuid string) error {
	fmt.Println("Getting storage")
	storages, err := s.GetStorages(&request.GetStoragesRequest{
		Access: upcloud.StorageAccessPrivate,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
		return err
	}
	fmt.Printf("Retrieved %d storages\n", len(storages.Storages))
	if len(storages.Storages) > 0 {
		for _, storage := range storages.Storages {
			if storage.UUID == uuid {
    		fmt.Printf("Deleting storage %s", storage.UUID)
				err := errors.New("Dummy")
				for i := 0; err != nil && i < 5; i++ {
					fmt.Printf("%d: Deleting %s (%s)\n", i, storage.Title, storage.UUID)
					err = s.DeleteStorage(&request.DeleteStorageRequest{
						UUID: storage.UUID,
					})
				}
				if err != nil {
					fmt.Fprintf(os.Stderr, "Unable to delete storage: %#v (%s)\n", err, err.Error())
					return err
				}

				fmt.Printf("Successfully deleted %s (%s)\n", storage.Title, storage.UUID)
			}
		}
	}
	return nil
}
func modifyStorage(s *service.Service, storecfg *StoreConfig) error {
	if len(storecfg.Uuid) == 0 {
		fmt.Printf("Unable to get storages uuid from data file \n")
		return nil
	}
	for _, item := range storecfg.Servers {
		s.StopServer(&request.StopServerRequest{
			UUID:     item,
			StopType: request.ServerStopTypeHard,
		})
  }
	fmt.Println("Getting storage")
	storages, err := s.GetStorages(&request.GetStoragesRequest{
		Access: upcloud.StorageAccessPrivate,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
		return err
	}
	fmt.Printf("Retrieved %d storages\n", len(storages.Storages))

	if len(storages.Storages) > 0 {
		for _, storage := range storages.Storages {
			if storage.UUID == storecfg.Uuid {
				fmt.Printf("Modify storage %s (%s)\n",storage.Title, storage.UUID)
				err := errors.New("Dummy")
				for i := 0; err != nil && i < 5; i++ {
					fmt.Printf("%d: Modify %s (%s)\n", i, storage.Title, storage.UUID)
					_, err = s.ModifyStorage(&request.ModifyStorageRequest{
						UUID: storecfg.Uuid,
						// Title: datacfg.Title,
						Size: storecfg.Size,
						// BackupRule *upcloud.BackupRule `json:"backup_rule,omitempty"`
					})
				}
				if err != nil {
					fmt.Fprintf(os.Stderr, "Unable to modify storage: %#v (%s)\n", err, err.Error())
					// return err
				}
				fmt.Printf("Successfully modified %s (%s)\n", storage.Title, storage.UUID)
			}
		}
	}
	for _, item := range storecfg.Servers {
		s.StartServer(&request.StartServerRequest{
			UUID:     item,
			// StopType: request.ServerStopTypeHard,
		})
  }
	return nil
}
 
func fixStorageDevices(s *service.Service,srvrCfg ServerConfig, serverDetails *upcloud.ServerDetails, targetSize string) {
   if len(serverDetails.StorageDevices) == 0  {
		return
	}
	err := errors.New("Dummy")
//    	fmt.Fprintf(os.Stderr, "%#v: %#v\n", serverDetails.StorageDevices, srvrCfg)
	for i, storage := range serverDetails.StorageDevices {
		store_size := 0
		switch targetSize {
			case "size":
				store_size = srvrCfg.StorageDevices[i].Size
			case "part0":
				s := strings.Split(srvrCfg.StorageDevices[i].PartSizes, ",")
				store_size, err = strconv.Atoi(s[0])
				if err != nil {
					fmt.Fprintf(os.Stderr, "Unable to get partsize 0 to create image storage for %s: %#v (%s)\n", storage.Title, err, err.Error())
					return 
				} 
			case "part1":
				s := strings.Split(srvrCfg.StorageDevices[i].PartSizes, ",")
				store_size, err = strconv.Atoi(s[1])
				if err != nil {
					fmt.Fprintf(os.Stderr, "Unable to get partsize 1 to create image storage for %s: %#v (%s)\n", storage.Title, err, err.Error())
					return 
				} 
			case "final":
				store_size = srvrCfg.StorageDevices[i].FinalSize
		}
		if store_size == 0 {
		   fmt.Printf("No targetSize found [size|final] for %s (%s) %dGB == %dGB\n",storage.Title, storage.UUID, storage.Size, store_size)	
			continue
		}
		if store_size== storage.Size { 
		  fmt.Printf("%s (%s) %dGB == %dGB\n",storage.Title, storage.UUID, storage.Size, store_size)	
			continue
		}
		fmt.Printf("FixStorage %s (%s) %dGB to %dGB\n",storage.Title, storage.UUID, storage.Size, store_size)	
		if serverDetails.Server.State != upcloud.ServerStateStopped {
			err = stopOneServer(s, serverDetails.Server) 
			if err != nil {
				fmt.Fprintf(os.Stderr, "Unable to stop server %#v: %#v\n", serverDetails.Server.UUID, err)
				continue
			}
		}
		_, err = s.ModifyStorage(&request.ModifyStorageRequest{
			UUID: storage.UUID,
			 //	Title: srvrCfg.StorageDevices[i].Title,
			Size: store_size,
			// BackupRule *upcloud.BackupRule `json:"backup_rule,omitempty"`
		})
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to modify storage: %#v (%s)\n", err, err.Error())
		} else {
			fmt.Printf("Successfully modified %s (%s) to %dGB\n", storage.Title, storage.UUID, store_size)
		}
	}
	info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
		UUID: serverDetails.UUID,
	})
	if info.Server.State != upcloud.ServerStateStarted {
		startOneServer(s, serverDetails.Server)
	}
}

func createStorageImage(s *service.Service, target_uuid string, title string) error {
	info,err := s.TemplatizeStorage(&request.TemplatizeStorageRequest{
		UUID: target_uuid,
		Title: strings.Trim(title, "\""),
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to create image storage: %#v (%s)\n", err, err.Error())
	} else {
		fmt.Printf("Successfully created %s (%s)\n", title, target_uuid)
	}
	s.WaitForStorageState(&request.WaitForStorageStateRequest{
		UUID:         target_uuid,
		DesiredState: "online",
		Timeout:      1 * time.Minute,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to wait for storage to reach desired online state: %#v", err)
		return err
	}
	enc := json.NewEncoder(os.Stdout)
	enc.Encode(info)
	return nil
}
func listStorages(s *service.Service, dataCfg *DataConfig, target string) error {
	storages, err := s.GetStorages(&request.GetStoragesRequest{
		Access: upcloud.StorageAccessPrivate,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
		return err
	}
	servers, _ := s.GetServers()
	fmt.Printf("Scanning over %d storages and %d servers \n", len(storages.Storages), len(servers.Servers))
	if len(storages.Storages) > 0 {
		for _, storage := range storages.Storages {
			storage_out := fmt.Sprintf("title: \"%s\", id: %s, size: %d, plan: %s, state: %s, zone: %s, type: %s",
				storage.Title, storage.UUID, storage.Size, storage.PartOfPlan, storage.State, storage.Zone, storage.Type)
			is_attached := false
			for _, server := range servers.Servers {
				isInGroup := false
				for _, srv := range dataCfg.Servers {
					if server.Hostname == srv.Hostname {
				           isInGroup = true
					   	break
					}
				}
			 	info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
					UUID: server.UUID,
			 	})
				if err == nil {
					for _, srv_storage := range info.StorageDevices {
					   if srv_storage.UUID == storage.UUID {
					     is_attached=true
					     if  isInGroup  {
					    	fmt.Printf("%s, attached: \"%s\", id: %s\n", storage_out, server.Title, server.UUID)
					     }
					   }
					}
				}
			}
  		if ! is_attached {
				fmt.Printf("%s, attached: none\n", storage_out)
			}
			if target != "" && storage.UUID == target {
				break
			}
		}
	}
	return nil
}