package server

import (
	"context"
	"fmt"
	"strings"

	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/pkg/formatter"
	"coopcloud.tech/libcapsul"
	"github.com/AlecAivazis/survey/v2"
	"github.com/hetznercloud/hcloud-go/hcloud"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

func newHetznerCloudVPS(c *cli.Context) error {
	if err := internal.EnsureNewHetznerCloudVPSFlags(c); err != nil {
		return err
	}

	client := hcloud.NewClient(hcloud.WithToken(internal.HetznerCloudAPIToken))

	var sshKeysRaw []string
	var sshKeys []*hcloud.SSHKey
	for _, sshKey := range c.StringSlice("hetzner-ssh-keys") {
		if sshKey == "" {
			continue
		}

		sshKey, _, err := client.SSHKey.GetByName(context.Background(), sshKey)
		if err != nil {
			return err
		}
		sshKeys = append(sshKeys, sshKey)
		sshKeysRaw = append(sshKeysRaw, sshKey.Name)
	}

	serverOpts := hcloud.ServerCreateOpts{
		Name:       internal.HetznerCloudName,
		ServerType: &hcloud.ServerType{Name: internal.HetznerCloudType},
		Image:      &hcloud.Image{Name: internal.HetznerCloudImage},
		SSHKeys:    sshKeys,
		Location:   &hcloud.Location{Name: internal.HetznerCloudLocation},
	}

	sshKeyIDs := strings.Join(sshKeysRaw, "\n")
	if sshKeyIDs == "" {
		sshKeyIDs = "N/A (password auth)"
	}

	tableColumns := []string{"name", "type", "image", "ssh-keys", "location"}
	table := formatter.CreateTable(tableColumns)
	table.Append([]string{
		internal.HetznerCloudName,
		internal.HetznerCloudType,
		internal.HetznerCloudImage,
		sshKeyIDs,
		internal.HetznerCloudLocation,
	})
	table.Render()

	response := false
	prompt := &survey.Confirm{
		Message: "continue with hetzner cloud VPS creation?",
	}

	if err := survey.AskOne(prompt, &response); err != nil {
		return err
	}

	if !response {
		logrus.Fatal("exiting as requested")
	}

	res, _, err := client.Server.Create(context.Background(), serverOpts)
	if err != nil {
		return err
	}

	var rootPassword string
	if len(sshKeys) > 0 {
		rootPassword = "N/A (using SSH keys)"
	} else {
		rootPassword = res.RootPassword
	}

	ip := res.Server.PublicNet.IPv4.IP.String()

	fmt.Println(fmt.Sprintf(`
Your new Hetzner Cloud VPS has successfully been created! Here are the details:

    name: %s
    IP address: %s
    root password: %s

You can access this new VPS via SSH using the following command:

    ssh root@%s

Please note, this server is not managed by Abra yet (i.e. "abra server ls" will
not list this server)! You will need to assign a domain name record (manually
or by using "abra record new") and add the server to your Abra configuration
("abra server add") to have a working server that you can deploy Co-op Cloud
apps to.

When setting up domain name records, you probably want to set up the following
2 A records. This supports deploying apps to your root domain (e.g.
example.com) and other apps on sub-domains (e.g. foo.example.com,
bar.example.com).

    @  1800 IN A %s
    *  1800 IN A %s
	`,
		internal.HetznerCloudName, ip, rootPassword,
		ip, ip, ip,
	))

	return nil
}

func newCapsulVPS(c *cli.Context) error {
	if err := internal.EnsureNewCapsulVPSFlags(c); err != nil {
		return err
	}

	capsulCreateURL := fmt.Sprintf("https://%s/api/capsul/create", internal.CapsulInstanceURL)

	var sshKeys []string
	for _, sshKey := range c.StringSlice("capsul-ssh-keys") {
		if sshKey == "" {
			continue
		}
		sshKeys = append(sshKeys, sshKey)
	}

	tableColumns := []string{"instance", "name", "type", "image", "ssh-keys"}
	table := formatter.CreateTable(tableColumns)
	table.Append([]string{
		internal.CapsulInstanceURL,
		internal.CapsulName,
		internal.CapsulType,
		internal.CapsulImage,
		strings.Join(sshKeys, "\n"),
	})
	table.Render()

	response := false
	prompt := &survey.Confirm{
		Message: "continue with capsul creation?",
	}

	if err := survey.AskOne(prompt, &response); err != nil {
		return err
	}

	if !response {
		logrus.Fatal("exiting as requested")
	}

	capsulClient := libcapsul.New(capsulCreateURL, internal.CapsulAPIToken)
	resp, err := capsulClient.Create(
		internal.CapsulName,
		internal.CapsulType,
		internal.CapsulImage,
		sshKeys,
	)
	if err != nil {
		return err
	}

	fmt.Println(fmt.Sprintf(`
Your new Capsul has successfully been created! Here are the details:

    Capsul name: %s
    Capsul ID:   %v

You will need to log into your Capsul instance web interface to retrieve the IP
address. You can learn all about how to get SSH access to your new Capsul on:

    %s/about-ssh

Please note, this server is not managed by Abra yet (i.e. "abra server ls" will
not list this server)! You will need to assign a domain name record (manually
or by using "abra record new") and add the server to your Abra configuration
("abra server add") to have a working server that you can deploy Co-op Cloud
apps to.

When setting up domain name records, you probably want to set up the following
2 A records. This supports deploying apps to your root domain (e.g.
example.com) and other apps on sub-domains (e.g. foo.example.com,
bar.example.com).

    @  1800 IN A <your-capsul-ip>
    *  1800 IN A <your-capsul-ip>
	`, internal.CapsulName, resp.ID, internal.CapsulInstanceURL))

	return nil
}

var serverNewCommand = cli.Command{
	Name:    "new",
	Aliases: []string{"n"},
	Usage:   "Create a new server using a 3rd party provider",
	Description: `
Create a new server via a 3rd party provider.

The following providers are supported:

		Capsul https://git.cyberia.club/Cyberia/capsul-flask
    Hetzner Cloud https://docs.hetzner.com/cloud

You may invoke this command in "wizard" mode and be prompted for input:

    abra record new

API tokens are read from the environment if specified, e.g.

    export HCLOUD_TOKEN=...
`,
	Flags: []cli.Flag{
		internal.DebugFlag,
		internal.NoInputFlag,
		internal.ServerProviderFlag,

		// Capsul
		internal.CapsulInstanceURLFlag,
		internal.CapsulTypeFlag,
		internal.CapsulImageFlag,
		internal.CapsulSSHKeysFlag,
		internal.CapsulAPITokenFlag,

		// Hetzner
		internal.HetznerCloudNameFlag,
		internal.HetznerCloudTypeFlag,
		internal.HetznerCloudImageFlag,
		internal.HetznerCloudSSHKeysFlag,
		internal.HetznerCloudLocationFlag,
		internal.HetznerCloudAPITokenFlag,
	},
	Before: internal.SubCommandBefore,
	Action: func(c *cli.Context) error {
		if err := internal.EnsureServerProvider(); err != nil {
			logrus.Fatal(err)
		}

		switch internal.ServerProvider {
		case "capsul":
			if err := newCapsulVPS(c); err != nil {
				logrus.Fatal(err)
			}
		case "hetzner-cloud":
			if err := newHetznerCloudVPS(c); err != nil {
				logrus.Fatal(err)
			}
		}

		return nil
	},
}