package service

import (
	"context"
	"strings"

	"github.com/keys-pub/keys"
	kapi "github.com/keys-pub/keys/api"
	"github.com/keys-pub/keys/user"
	"github.com/pkg/errors"
)

// lookupOpts are options for key lookups.
type lookupOpts struct {
	// VerifyUser makes sure the user is verified.
	VerifyUser bool
	// SearchRemote to look for the key on the server if not found locally.
	SearchRemote bool
}

// lookup a key by kid or user@service.
func (s *service) lookup(ctx context.Context, key string, opts *lookupOpts) (keys.ID, error) {
	if key == "" {
		return "", errors.Errorf("no key specified")
	}
	if opts == nil {
		opts = &lookupOpts{}
	}

	kid, err := s.lookupKID(ctx, key, opts.SearchRemote)
	if err != nil {
		return "", err
	}
	if kid == "" {
		return "", keys.NewErrNotFound(key)
	}

	if opts.VerifyUser {
		res, err := s.users.Get(ctx, kid)
		if err != nil {
			return "", err
		}
		if res == nil {
			return kid, nil
		}
		if res.Status != user.StatusOK {
			return "", errors.Errorf("user %s has failed status %s", res.User.ID(), res.Status)
		}
	}

	return kid, nil
}

func (s *service) lookupAll(ctx context.Context, ks []string, opts *lookupOpts) ([]keys.ID, error) {
	ids := make([]keys.ID, 0, len(ks))
	for _, key := range ks {
		id, err := s.lookup(ctx, key, opts)
		if err != nil {
			return nil, err
		}
		ids = append(ids, id)
	}
	return ids, nil
}

func (s *service) lookupKID(ctx context.Context, key string, searchRemote bool) (keys.ID, error) {
	if strings.Contains(key, "@") {
		return s.lookupUser(ctx, key, searchRemote)
	}

	kid, err := keys.ParseID(key)
	if err != nil {
		return "", errors.Errorf("failed to parse %s", key)
	}

	rkid, err := s.scs.Lookup(kid)
	if err != nil {
		return "", err
	}
	if rkid != "" {
		kid = rkid
	}

	if searchRemote {
		res, err := s.client.User(ctx, kid)
		if err != nil {
			return "", err
		}
		if res != nil {
			// TODO: We should verify ourselves that the server isn't lying
			return res.User.KID, nil
		}
	}
	return kid, nil
}

func (s *service) lookupKey(ctx context.Context, key string, opts *lookupOpts) (*kapi.Key, error) {
	kid, err := s.lookup(ctx, key, nil)
	if err != nil {
		return nil, err
	}
	k, err := s.vaultKey(kid)
	if err != nil {
		return nil, err
	}
	return k, nil
}
