package command

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strconv"

	"codeberg.org/hyperreal/go-torrent-helper/almalinux"
	"codeberg.org/hyperreal/go-torrent-helper/debian"
	"codeberg.org/hyperreal/go-torrent-helper/devuan"
	"codeberg.org/hyperreal/go-torrent-helper/fedora"
	"codeberg.org/hyperreal/go-torrent-helper/freebsd"
	"codeberg.org/hyperreal/go-torrent-helper/kali"
	"codeberg.org/hyperreal/go-torrent-helper/netbsd"
	"codeberg.org/hyperreal/go-torrent-helper/nixos"
	"codeberg.org/hyperreal/go-torrent-helper/parrot"
	"codeberg.org/hyperreal/go-torrent-helper/qubes"
	"codeberg.org/hyperreal/go-torrent-helper/rocky"
	"codeberg.org/hyperreal/go-torrent-helper/tails"
	"github.com/hekmon/transmissionrpc"
)

type Distro interface {
	AddNewTorrents(client *transmissionrpc.Client) error
	RemoveOldTorrents(client *transmissionrpc.Client) error
}

func NewAddCmd() *AddCmd {
	addCmd := &AddCmd{
		fs: flag.NewFlagSet("add", flag.ExitOnError),
	}

	addCmd.fs.StringVar(&addCmd.distro, "distro", "", "Distro to add torrents for")
	addCmd.fs.StringVar(&addCmd.relver, "relver", "", "Release version to add")

	return addCmd
}

func NewRemoveCmd() *RemoveCmd {
	removeCmd := &RemoveCmd{
		fs: flag.NewFlagSet("remove", flag.ExitOnError),
	}

	removeCmd.fs.StringVar(&removeCmd.distro, "distro", "", "Distro to remove torrents for")
	removeCmd.fs.StringVar(&removeCmd.relver, "relver", "", "Release version to remove")

	return removeCmd
}

func NewListCmd() *ListCmd {
	listCmd := &ListCmd{
		fs: flag.NewFlagSet("list", flag.ExitOnError),
	}

	listCmd.fs.Bool("list", false, "List supported distributions")

	return listCmd
}

type AddCmd struct {
	fs *flag.FlagSet

	distro string
	relver string
}

type RemoveCmd struct {
	fs *flag.FlagSet

	distro string
	relver string
}

type ListCmd struct {
	fs *flag.FlagSet
}

func (a *AddCmd) Name() string {
	return a.fs.Name()
}

func (r *RemoveCmd) Name() string {
	return r.fs.Name()
}

func (l *ListCmd) Name() string {
	return l.fs.Name()
}

func (a *AddCmd) Init(args []string) error {
	return a.fs.Parse(args)
}

func (r *RemoveCmd) Init(args []string) error {
	return r.fs.Parse(args)
}

func (l *ListCmd) Init(args []string) error {
	return l.fs.Parse(args)
}

func (a *AddCmd) Run(d Distro) error {
	txRpcUrl := os.Getenv("TX_RPC_URL")
	txRpcUser := os.Getenv("TX_RPC_USER")
	txRpcPass := os.Getenv("TX_RPC_PASS")
	txRpcPort, err := strconv.ParseUint(os.Getenv("TX_RPC_PORT"), 10, 16)
	if err != nil {
		return err
	}
	txRpcUseHTTPS, err := strconv.ParseBool(os.Getenv("TX_RPC_USE_HTTPS"))
	if err != nil {
		return err
	}

	client, err := transmissionrpc.New(txRpcUrl, txRpcUser, txRpcPass,
		&transmissionrpc.AdvancedConfig{
			HTTPS: txRpcUseHTTPS,
			Port:  uint16(txRpcPort),
		},
	)
	if err != nil {
		return err
	}

	if err := d.AddNewTorrents(client); err != nil {
		return err
	}

	return nil
}

func (r *RemoveCmd) Run(d Distro) error {
	txRpcUrl := os.Getenv("TX_RPC_URL")
	txRpcUser := os.Getenv("TX_RPC_USER")
	txRpcPass := os.Getenv("TX_RPC_PASS")
	txRpcPort, err := strconv.ParseUint(os.Getenv("TX_RPC_PORT"), 10, 16)
	if err != nil {
		return err
	}
	txRpcUseHTTPS, err := strconv.ParseBool(os.Getenv("TX_RPC_USE_HTTPS"))
	if err != nil {
		return err
	}

	client, err := transmissionrpc.New(txRpcUrl, txRpcUser, txRpcPass,
		&transmissionrpc.AdvancedConfig{
			HTTPS: txRpcUseHTTPS,
			Port:  uint16(txRpcPort),
		},
	)
	if err != nil {
		return err
	}

	if err := d.RemoveOldTorrents(client); err != nil {
		return err
	}

	return nil
}

func (l *ListCmd) Run(d Distro) error {
	supportedDistros := []string{
		"almalinux",
		"debian",
		"devuan",
		"fedora",
		"freebsd",
		"kali",
		"netbsd",
		"nixos",
		"parrot",
		"qubes",
		"rocky",
		"tails",
	}

	fmt.Println("Supported distributions:")
	for _, v := range supportedDistros {
		fmt.Printf("* %s\n", v)
	}

	return nil
}

type Runner interface {
	Init([]string) error
	Run(d Distro) error
	Name() string
}

func Root(args []string) error {
	if len(args) < 1 {
		return errors.New("you must pass a sub-command")
	}

	cmds := []Runner{
		NewAddCmd(),
		NewRemoveCmd(),
		NewListCmd(),
	}

	subcmd := os.Args[1]

	for _, cmd := range cmds {
		if cmd.Name() == subcmd {

			if subcmd == "list" {
				return cmd.Run(nil)
			}

			var distro, relver string

			if len(os.Args[2:]) == 0 {
				return fmt.Errorf("see -h for usage options")
			}

			cmd.Init(os.Args[2:])

			if len(os.Args[4:]) == 0 && subcmd == "remove" {
				relver = "*"
			} else if len(os.Args[4:]) == 0 && subcmd == "add" {
				return fmt.Errorf("please enter a release version")
			} else {
				relver = os.Args[5]
			}

			distro = os.Args[3]

			switch distro {
			case "almalinux":
				a := &almalinux.AlmaLinux{NameSubstr: "AlmaLinux", Relver: relver}
				return cmd.Run(a)

			case "debian":
				d := &debian.Debian{NameSubstr: "debian", Relver: relver}
				return cmd.Run(d)

			case "devuan":
				dv := &devuan.Devuan{NameSubstr: "devuan", Relver: relver}
				return cmd.Run(dv)

			case "fedora":
				f := &fedora.Fedora{NameSubstr: "Fedora", Relver: relver}
				return cmd.Run(f)

			case "freebsd":
				fb := &freebsd.FreeBSD{NameSubstr: "FreeBSD", Relver: relver}
				return cmd.Run(fb)

			case "kali":
				k := &kali.Kali{NameSubstr: "kali-linux", Relver: relver}
				return cmd.Run(k)

			case "netbsd":
				nb := &netbsd.NetBSD{NameSubstr: "NetBSD", Relver: relver}
				return cmd.Run(nb)

			case "nixos":
				n := &nixos.Nixos{NameSubstr: "nixos"}
				return cmd.Run(n)

			case "parrot":
				p := &parrot.Parrot{NameSubstr: "Parrot", Relver: relver}
				return cmd.Run(p)

			case "qubes":
				q := &qubes.Qubes{NameSubstr: "Qubes", Relver: relver}
				return cmd.Run(q)

			case "rocky":
				r := &rocky.Rocky{NameSubstr: "Rocky", Relver: relver}
				return cmd.Run(r)

			case "tails":
				t := &tails.Tails{NameSubstr: "Tails", Relver: relver}
				return cmd.Run(t)

			default:
				return fmt.Errorf("unknown distro: %s", distro)
			}
		}
	}

	return fmt.Errorf("unknown subcommand: %s", subcmd)
}
