package main

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/signal"

	"github.com/urfave/cli/v3"
)

var app = cli.Command{
	Name:  "chord",
	Usage: "Distributed hash table using the Chord protocol",

	Commands: []*cli.Command{
		{
			Name:    "join",
			Aliases: []string{"init", "initialize"},
			Usage:   "Join or initialize a Chord network.",
			Flags: []cli.Flag{
				flagIP,
				flagPort,
				&cli.StringFlag{
					Name:  "c",
					Usage: "Address of an existing node to join.",
				},
			},
			Action: cliJoin,
		},
		{
			Name:  "leave",
			Usage: "Instruct a node to leave the network.",
			Flags: []cli.Flag{
				flagIP,
				flagPort,
				&cli.StringFlag{
					Name:     "n",
					Usage:    "Node to leave.",
					Required: true,
				},
			},
			Action: cliLeave,
		},
		{
			Name:  "lookup",
			Usage: "Find the node responsible for a key.",
			Flags: []cli.Flag{
				flagKey,
				flagNode,
				flagLocal,
			},
			Action: cliLookup,
		},
		{
			Name:    "put",
			Aliases: []string{"store", "set"},
			Usage:   "Set a value in the network.",
			Flags: []cli.Flag{
				flagKey,
				&cli.StringFlag{
					Name:     "v",
					Usage:    "Value to set.",
					Required: true,
				},
				flagNode,
				flagLocal,
			},
			Action: cliPut,
		},
		{
			Name:    "get",
			Aliases: []string{"load"},
			Usage:   "Get a value in the network.",
			Flags: []cli.Flag{
				flagKey,
				flagNode,
				flagLocal,
			},
			Action: cliGet,
		},
	},
}

var (
	flagIP = &cli.StringFlag{
		Name:  "ip",
		Usage: "IP to bind. May include port.",
		Value: "127.0.0.1:3000",
	}
	flagPort = &cli.UintFlag{
		Name:  "p",
		Usage: "Port to bind. Overrides port in -ip if given.",
	}
	flagNode = &cli.StringFlag{
		Name:     "n",
		Usage:    "Address of any node in the network.",
		Required: true,
	}
	flagKey = &cli.StringFlag{
		Name:     "k",
		Usage:    "Key to operate on.",
		Required: true,
	}
	flagLocal = &cli.BoolFlag{
		Name:  "l",
		Usage: "Stop if the key is not local to the node.",
	}
)

func cliJoin(ctx context.Context, cmd *cli.Command) error   { return errors.New("not implemented") }
func cliLeave(ctx context.Context, cmd *cli.Command) error  { return errors.New("not implemented") }
func cliLookup(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") }
func cliPut(ctx context.Context, cmd *cli.Command) error    { return errors.New("not implemented") }
func cliGet(ctx context.Context, cmd *cli.Command) error    { return errors.New("not implemented") }

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	go func() {
		<-ctx.Done()
		stop()
	}()
	if err := app.Run(ctx, os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}