package service

import (
	"bufio"
	"context"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/keys-pub/keys"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

func sigchainCommands(client *Client) []cli.Command {
	return []cli.Command{
		cli.Command{
			Name:  "sigchain",
			Usage: "Sigchains",
			Subcommands: []cli.Command{
				cli.Command{
					Name:  "show",
					Usage: "Show sigchain",
					Flags: []cli.Flag{
						cli.StringFlag{Name: "kid, k", Usage: "kid"},
						cli.IntFlag{Name: "seq, s", Usage: "seq"},
					},
					Action: func(c *cli.Context) error {
						kida, err := argString(c, "kid", false)
						if err != nil {
							return err
						}
						seq := c.Int("seq")
						if seq != 0 {
							resp, err := client.RPCClient().Statement(context.TODO(), &StatementRequest{
								KID: kida,
								Seq: int32(seq),
							})
							if err != nil {
								return err
							}
							st, err := statementFromRPC(resp.Statement)
							if err != nil {
								return err
							}
							b, err := st.Bytes()
							if err != nil {
								return err
							}
							fmt.Println(string(b))
							return nil
						}

						resp, err := client.RPCClient().Sigchain(context.TODO(), &SigchainRequest{
							KID: kida,
						})
						if err != nil {
							return err
						}

						kid, err := keys.ParseID(resp.Key.ID)
						if err != nil {
							return err
						}

						logger.Infof("Resolving statements")
						sc, err := sigchainFromRPC(kid, resp.Statements)
						if err != nil {
							return err
						}
						for _, st := range sc.Statements() {
							b, err := st.Bytes()
							if err != nil {
								return err
							}
							fmt.Println(string(b))
						}
						return nil
					},
				},
				cli.Command{
					Name:  "statement",
					Usage: "Sigchain statements",
					Subcommands: []cli.Command{
						cli.Command{
							Name:      "add",
							Usage:     "Add a signed statement to a sigchain (from stdin)",
							ArgsUsage: "stdin",
							Flags: []cli.Flag{
								cli.StringFlag{Name: "kid, k"},
								cli.BoolFlag{Name: "local", Usage: "Don't save to the key server"},
							},
							Action: func(c *cli.Context) error {
								if c.NArg() > 0 {
									return errors.Errorf("input is from stdin, not as an argument")
								}

								r := bufio.NewReader(os.Stdin)
								b, err := ioutil.ReadAll(r)
								if err != nil {
									return err
								}
								if len(b) > 16*1024 {
									return errors.Errorf("sigchain data restricted to 16KB")
								}

								resp, err := client.RPCClient().StatementCreate(context.TODO(), &StatementCreateRequest{
									KID:   c.String("kid"),
									Data:  b,
									Local: c.Bool("local"),
								})
								if err != nil {
									return err
								}
								st, err := statementFromRPC(resp.Statement)
								if err != nil {
									return err
								}
								sb, err := st.Bytes()
								if err != nil {
									return err
								}
								fmt.Printf("%s\n", string(sb))
								return nil
							},
						},
						cli.Command{
							Name:  "revoke",
							Usage: "Revoke a signed statement in a sigchain",
							Flags: []cli.Flag{
								cli.StringFlag{Name: "kid, k"},
								cli.IntFlag{Name: "seq, s"},
								cli.BoolFlag{Name: "local", Usage: "Don't save to the key server"},
							},
							Action: func(c *cli.Context) error {
								kid, err := argString(c, "kid", false)
								if err != nil {
									return err
								}
								resp, err := client.RPCClient().StatementRevoke(context.TODO(), &StatementRevokeRequest{
									KID:   kid,
									Seq:   int32(c.Int("seq")),
									Local: c.Bool("local"),
								})
								if err != nil {
									return err
								}
								st, err := statementFromRPC(resp.Statement)
								if err != nil {
									return err
								}
								b, err := st.Bytes()
								if err != nil {
									return err
								}
								fmt.Printf("%s\n", string(b))
								return nil
							},
						},
					},
				},
			},
		},
	}
}
