package keybase

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"samhofi.us/x/keybase/v2/types/chat1"
)

// Used for testing
var execCommand = exec.Command

// Possible MemberTypes
const (
	TEAM string = "team"
	USER string = "impteamnative"
)

// Possible TopicTypes
const (
	DEV  string = "dev"
	CHAT string = "chat"
)

// New returns a new Keybase
func New(opts ...KeybaseOpt) *Keybase {
	k := &Keybase{ExePath: "keybase"}

	for _, opt := range opts {
		opt.apply(k)
	}

	s := k.status()
	k.Version = k.version()
	k.LoggedIn = s.LoggedIn
	if k.LoggedIn {
		k.Username = s.Username
		k.Device = s.Device.Name
	}

	return k
}

// NewKeybase returns a new Keybase. Optionally, you can pass a string containing the path to the Keybase executable as the first argument.
// This is deprecated and will be removed in a future update. Use New() instead.
func NewKeybase(path ...string) *Keybase {
	k := &Keybase{}
	if len(path) < 1 {
		k.ExePath = "keybase"
	} else {
		k.ExePath = path[0]
	}

	s := k.status()
	k.Version = k.version()
	k.LoggedIn = s.LoggedIn
	if k.LoggedIn {
		k.Username = s.Username
		k.Device = s.Device.Name
	}
	return k
}

// Exec executes the given Keybase command
func (k *Keybase) Exec(command ...string) ([]byte, error) {
	cmd := make([]string, 0)

	if k.HomePath != "" {
		cmd = append(cmd, "--home", k.HomePath)
	}

	cmd = append(cmd, command...)

	out, err := execCommand(k.ExePath, cmd...).Output()
	if err != nil {
		return []byte{}, err
	}
	return out, nil
}

// NewChat returns a new Chat instance
func (k *Keybase) NewChat(channel chat1.ChatChannel) Chat {
	return Chat{
		keybase: k,
		Channel: channel,
	}
}

// NewTeam returns a new Team instance
func (k *Keybase) NewTeam(name string) Team {
	return Team{
		keybase: k,
		Name:    name,
	}
}

// NewWallet returns a new Wallet instance
func (k *Keybase) NewWallet() Wallet {
	return Wallet{
		keybase: k,
	}
}

// status returns the results of the `keybase status` command, which includes
// information about the client, and the currently logged-in Keybase user.
func (k *Keybase) status() status {
	cmdOut, err := k.Exec("status", "-j")
	if err != nil {
		return status{}
	}

	var s status
	json.Unmarshal(cmdOut, &s)

	return s
}

// version returns the version string of the client.
func (k *Keybase) version() string {
	cmdOut, err := k.Exec("version", "-S", "-f", "s")
	if err != nil {
		return ""
	}

	return string(cmdOut)
}

// UserLookup pulls information about users.
// The following fields are currently returned: basics, profile, proofs_summary, devices -- See https://keybase.io/docs/api/1.0/call/user/lookup for more info.
func (k *Keybase) UserLookup(users ...string) (UserAPI, error) {
	var fields = []string{"basics", "profile", "proofs_summary", "devices"}

	cmdOut, err := k.Exec("apicall", "--arg", fmt.Sprintf("usernames=%s", strings.Join(users, ",")), "--arg", fmt.Sprintf("fields=%s", strings.Join(fields, ",")), "user/lookup")
	if err != nil {
		return UserAPI{}, err
	}

	var r UserAPI
	if err := json.Unmarshal(cmdOut, &r); err != nil {
		return UserAPI{}, err
	}

	return r, nil
}

// UserCard pulls the information that is typically displayed when you open a user's profile.
func (k *Keybase) UserCard(user string) (UserCardAPI, error) {
	cmdOut, err := k.Exec("apicall", "--arg", "username="+user, "user/card")
	if err != nil {
		return UserCardAPI{}, err
	}

	var r UserCardAPI
	if err := json.Unmarshal(cmdOut, &r); err != nil {
		return UserCardAPI{}, err
	}

	return r, nil
}