package main

import (
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/ipfs/go-cid"
	"github.com/urfave/cli/v2"

	lcli "github.com/filecoin-project/lotus/cli"
	"github.com/filecoin-project/lotus/lib/tablewriter"
)

var piecesCmd = &cli.Command{
	Name:        "pieces",
	Usage:       "interact with the piecestore",
	Description: "The piecestore is a database that tracks and manages data that is made available to the retrieval market",
	Subcommands: []*cli.Command{
		piecesListPiecesCmd,
		piecesListCidInfosCmd,
		piecesInfoCmd,
		piecesCidInfoCmd,
	},
}

var piecesListPiecesCmd = &cli.Command{
	Name:  "list-pieces",
	Usage: "list registered pieces",
	Action: func(cctx *cli.Context) error {
		nodeApi, closer, err := lcli.GetMarketsAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := lcli.ReqContext(cctx)

		pieceCids, err := nodeApi.PiecesListPieces(ctx)
		if err != nil {
			return err
		}

		for _, pc := range pieceCids {
			fmt.Println(pc)
		}
		return nil
	},
}

var piecesListCidInfosCmd = &cli.Command{
	Name:  "list-cids",
	Usage: "list registered payload CIDs",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "verbose",
			Aliases: []string{"v"},
		},
	},
	Action: func(cctx *cli.Context) error {
		nodeApi, closer, err := lcli.GetMarketsAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := lcli.ReqContext(cctx)

		cids, err := nodeApi.PiecesListCidInfos(ctx)
		if err != nil {
			return err
		}

		w := tablewriter.New(tablewriter.Col("CID"),
			tablewriter.Col("Piece"),
			tablewriter.Col("BlockOffset"),
			tablewriter.Col("BlockLen"),
			tablewriter.Col("Deal"),
			tablewriter.Col("Sector"),
			tablewriter.Col("DealOffset"),
			tablewriter.Col("DealLen"),
		)

		for _, c := range cids {
			if !cctx.Bool("verbose") {
				fmt.Println(c)
				continue
			}

			ci, err := nodeApi.PiecesGetCIDInfo(ctx, c)
			if err != nil {
				fmt.Printf("Error getting CID info: %s\n", err)
				continue
			}

			for _, location := range ci.PieceBlockLocations {
				pi, err := nodeApi.PiecesGetPieceInfo(ctx, location.PieceCID)
				if err != nil {
					fmt.Printf("Error getting piece info: %s\n", err)
					continue
				}

				for _, deal := range pi.Deals {
					w.Write(map[string]interface{}{
						"CID":         c,
						"Piece":       location.PieceCID,
						"BlockOffset": location.RelOffset,
						"BlockLen":    location.BlockSize,
						"Deal":        deal.DealID,
						"Sector":      deal.SectorID,
						"DealOffset":  deal.Offset,
						"DealLen":     deal.Length,
					})
				}
			}
		}

		if cctx.Bool("verbose") {
			return w.Flush(os.Stdout)
		}

		return nil
	},
}

var piecesInfoCmd = &cli.Command{
	Name:  "piece-info",
	Usage: "get registered information for a given piece CID",
	Action: func(cctx *cli.Context) error {
		if !cctx.Args().Present() {
			return lcli.ShowHelp(cctx, fmt.Errorf("must specify piece cid"))
		}

		nodeApi, closer, err := lcli.GetMarketsAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := lcli.ReqContext(cctx)

		c, err := cid.Decode(cctx.Args().First())
		if err != nil {
			return err
		}

		pi, err := nodeApi.PiecesGetPieceInfo(ctx, c)
		if err != nil {
			return err
		}

		fmt.Println("Piece: ", pi.PieceCID)
		w := tabwriter.NewWriter(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintln(w, "Deals:\nDealID\tSectorID\tLength\tOffset")
		for _, d := range pi.Deals {
			fmt.Fprintf(w, "%d\t%d\t%d\t%d\n", d.DealID, d.SectorID, d.Length, d.Offset)
		}
		return w.Flush()
	},
}

var piecesCidInfoCmd = &cli.Command{
	Name:  "cid-info",
	Usage: "get registered information for a given payload CID",
	Action: func(cctx *cli.Context) error {
		if !cctx.Args().Present() {
			return lcli.ShowHelp(cctx, fmt.Errorf("must specify payload cid"))
		}

		nodeApi, closer, err := lcli.GetMarketsAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := lcli.ReqContext(cctx)

		c, err := cid.Decode(cctx.Args().First())
		if err != nil {
			return err
		}

		ci, err := nodeApi.PiecesGetCIDInfo(ctx, c)
		if err != nil {
			return err
		}

		fmt.Println("Info for: ", ci.CID)

		w := tabwriter.NewWriter(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "PieceCid\tOffset\tSize\n")
		for _, loc := range ci.PieceBlockLocations {
			fmt.Fprintf(w, "%s\t%d\t%d\n", loc.PieceCID, loc.RelOffset, loc.BlockSize)
		}
		return w.Flush()
	},
}