package main

import (
	"bytes"
	"context"
	"encoding/hex"
	"fmt"
	"log"
	"time"

	"cloud.google.com/go/bigtable"
	"google.golang.org/api/option"
)

type Signature struct {
	// Index of the validator
	Index uint8
	// Signature data
	Signature [65]byte
}

func printRow(row bigtable.Row) {
	if _, ok := row[columnFamilies[0]]; ok {
		printItems(row[columnFamilies[0]])
	}
	if _, ok := row[columnFamilies[3]]; ok {
		printItems(row[columnFamilies[3]])
	}
}

func printItems(familyCols []bigtable.ReadItem) {
	for _, item := range familyCols {

		log.Printf("\t%s = %s\n", item.Column, string(item.Value))
	}
}

func printSignatures(familyCols []bigtable.ReadItem) {
	for _, item := range familyCols {
		if item.Column == "VAAState:Signatures" {
			reader := bytes.NewReader(item.Value[:])
			lenSignatures, er := reader.ReadByte()
			if er != nil {
				log.Print(fmt.Errorf("failed to read signature length"))
				return
			}

			signatures := make([]*Signature, lenSignatures)
			for i := 0; i < int(lenSignatures); i++ {
				index, err := reader.ReadByte()
				if err != nil {
					log.Print(fmt.Errorf("failed to read validator index [%d]", i))
					return
				}

				signature := [65]byte{}
				if n, err := reader.Read(signature[:]); err != nil || n != 65 {
					log.Print(fmt.Errorf("failed to read signature [%d]: %w", i, err))
					return
				}

				signatures[i] = &Signature{
					Index:     index,
					Signature: signature,
				}
			}
			for index, sig := range signatures {
				log.Printf("\tSignatures: list index: %v, item index: %v, signature = %s\n", index, sig.Index, hex.EncodeToString(sig.Signature[:]))
			}
		} else {
			log.Printf("\t%s = %s\n", item.Column, string(item.Value))
		}
	}
}

// Query will lookup BigTable row(s) and log their data.
func Query(project string, instance string, keyFilePath string, rowKey string, previousMinutes int) {

	ctx := context.Background()

	client, err := bigtable.NewClient(ctx, project, instance, option.WithCredentialsFile(keyFilePath))
	if err != nil {
		log.Fatalf("Could not create data operations client: %v", err)
	}

	tbl := client.Open(tableName)

	if rowKey != "" {
		log.Printf("Querying by row key: %s ", rowKey)
		row, err := tbl.ReadRow(ctx, rowKey)
		if err != nil {
			log.Fatalf("Could not read row with key %s: %v", rowKey, err)
		}

		printRow(row)
	}

	if previousMinutes != 0 {
		xMinutesAgo := time.Now().Add(-time.Duration(previousMinutes) * time.Minute)
		log.Printf("Reading rows from: %v ", xMinutesAgo)

		err = tbl.ReadRows(ctx, bigtable.PrefixRange(""), func(row bigtable.Row) bool {
			if _, ok := row[columnFamilies[0]]; ok {
				// log the rowKey
				cell := row[columnFamilies[0]][0]
				log.Printf("rowKey: %s", cell.Row)
			}
			printRow(row)
			return true
		}, bigtable.RowFilter(bigtable.TimestampRangeFilter(xMinutesAgo, time.Now())))
		if err != nil {
			log.Fatalf("failed to read recent rows: %v", err)
		}
	}

	if err = client.Close(); err != nil {
		log.Fatalf("Could not close data operations client: %v", err)
	}
}
