package service

import (
	"context"

	"github.com/keys-pub/keys"
	"github.com/keys-pub/keys/user"
	"github.com/keys-pub/keys/user/services"
	"github.com/keys-pub/keys/users"
	"github.com/pkg/errors"
)

func (s *service) checkKeys(ctx context.Context) error {
	logger.Infof("Checking keys...")
	pks, err := s.vault.EdX25519PublicKeys()
	if err != nil {
		return errors.Wrapf(err, "failed to list public keys")
	}
	for _, pk := range pks {
		if err := ctx.Err(); err != nil {
			return err
		}
		// We only need to do this on key creation or after a sigchain update,
		// but old versions have never sigchain indexed before, so we'll do this
		// here every time.
		if err := s.scs.Index(pk.ID()); err != nil {
			return err
		}

		if err := s.checkForExpiredKey(ctx, pk.ID()); err != nil {
			return err
		}
	}
	return nil
}

// Check if expired, and then update.
// If we don't have a local result, we don't update.
func (s *service) checkForExpiredKey(ctx context.Context, kid keys.ID) error {
	res, err := s.users.Get(ctx, kid)
	if err != nil {
		return err
	}
	if res != nil {
		// If not OK, check every "userCheckFailureExpire", otherwise check every "userCheckExpire".
		now := s.clock.Now()
		if (res.Status != user.StatusOK && res.IsTimestampExpired(now, userCheckFailureExpire)) ||
			res.IsTimestampExpired(now, userCheckExpire) {
			_, err := s.updateUser(ctx, kid, true)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (s *service) updateAllKeys(ctx context.Context) error {
	logger.Infof("Updating keys...")
	pks, err := s.vault.EdX25519PublicKeys()
	if err != nil {
		return err
	}
	for _, pk := range pks {
		if _, err := s.updateUser(ctx, pk.ID(), true); err != nil {
			return err
		}
	}
	return nil
}

func (s *service) updateUser(ctx context.Context, kid keys.ID, allowProxyCache bool) (*user.Result, error) {
	logger.Infof("Update user %s", kid)

	// TODO: Only get new sigchain entries.
	resp, err := s.client.Sigchain(ctx, kid)
	if err != nil {
		return nil, err
	}
	if resp != nil {
		// TODO: Check that our existing statements haven't changed or disappeared
		logger.Infof("Received sigchain %s, len=%d", kid, len(resp.Statements))

		sc := keys.NewSigchain(kid)
		if err := sc.AddAll(resp.Statements); err != nil {
			return nil, err
		}
		if err := s.scs.Save(sc); err != nil {
			return nil, err
		}
	} else {
		logger.Infof("No sigchain for %s", kid)
	}

	if err := s.scs.Index(kid); err != nil {
		return nil, err
	}

	service := func(usr *user.User) services.Service {
		switch usr.Service {
		case "twitter":
			if allowProxyCache {
				return services.KeysPub
			}
			return services.Proxy
		}
		return nil
	}

	res, err := s.users.Update(ctx, kid, users.UseService(service))
	if err != nil {
		return nil, err
	}

	return res, nil
}

func twitterProxy(usr *user.User) services.Service {
	switch usr.Service {
	case "twitter":
		return services.Proxy
	}
	return nil
}
