package cli

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/internal/cli/flagset"
	"github.com/ethereum/go-ethereum/internal/cli/server/proto"
)

// ChainWatchCommand is the command to group the peers commands
type ChainWatchCommand struct {
	*Meta2
}

// MarkDown implements cli.MarkDown interface
func (c *ChainWatchCommand) MarkDown() string {
	items := []string{
		"# Chain watch",
		"The ```chain watch``` command is used to view the chainHead, reorg and fork events in real-time.",
	}

	return strings.Join(items, "\n\n")
}

// Help implements the cli.Command interface
func (c *ChainWatchCommand) Help() string {
	return `Usage: bor chain watch

  This command is used to view the chainHead, reorg and fork events in real-time`
}

func (c *ChainWatchCommand) Flags() *flagset.Flagset {
	flags := c.NewFlagSet("chain watch")

	return flags
}

// Synopsis implements the cli.Command interface
func (c *ChainWatchCommand) Synopsis() string {
	return "Watch the chainHead, reorg and fork events in real-time"
}

// Run implements the cli.Command interface
func (c *ChainWatchCommand) Run(args []string) int {
	flags := c.Flags()
	if err := flags.Parse(args); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	borClt, err := c.BorConn()
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	sub, err := borClt.ChainWatch(context.Background(), &proto.ChainWatchRequest{})
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-signalCh

		if err := sub.CloseSend(); err != nil {
			c.UI.Error(err.Error())
		}
	}()

	for {
		msg, err := sub.Recv()
		if err != nil {
			// if err == EOF if finished on the other side
			c.UI.Output(err.Error())
			break
		}

		c.UI.Output(formatHeadEvent(msg))
	}

	return 0
}

func formatHeadEvent(msg *proto.ChainWatchResponse) string {
	var out string
	if msg.Type == core.Chain2HeadCanonicalEvent {
		out = fmt.Sprintf("Block Added : %v", msg.Newchain)
	} else if msg.Type == core.Chain2HeadForkEvent {
		out = fmt.Sprintf("New Fork Block : %v", msg.Newchain)
	} else if msg.Type == core.Chain2HeadReorgEvent {
		out = fmt.Sprintf("Reorg Detected \nAdded : %v \nRemoved : %v", msg.Newchain, msg.Oldchain)
	}

	return out
}
