// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package core

import (
	"context"
	"errors"

	"github.com/asaskevich/govalidator"
)

var (
	errUsernameLen  = errors.New("Invalid username length")
	errUsernameChar = errors.New("Invalid character in username")
)

type (
	// User represents a user of the system.
	User struct {
		ID        int64  `json:"id"`
		Login     string `json:"login"`
		Email     string `json:"email"`
		Machine   bool   `json:"machine"`
		Admin     bool   `json:"admin"`
		Active    bool   `json:"active"`
		Avatar    string `json:"avatar"`
		Syncing   bool   `json:"syncing"`
		Synced    int64  `json:"synced"`
		Created   int64  `json:"created"`
		Updated   int64  `json:"updated"`
		LastLogin int64  `json:"last_login"`
		Token     string `json:"-"`
		Refresh   string `json:"-"`
		Expiry    int64  `json:"-"`
		Hash      string `json:"-"`
	}

	// UserParams defines user query parameters.
	UserParams struct {
		// Sort instructs the system to sort by Login if true,
		// else sort by primary key.
		Sort bool

		Page int64
		Size int64
	}

	// UserStore defines operations for working with users.
	UserStore interface {
		// Find returns a user from the datastore.
		Find(context.Context, int64) (*User, error)

		// FindLogin returns a user from the datastore by username.
		FindLogin(context.Context, string) (*User, error)

		// FindToken returns a user from the datastore by token.
		FindToken(context.Context, string) (*User, error)

		// List returns a list of users from the datastore.
		List(context.Context) ([]*User, error)

		// ListRange returns a range of users from the datastore.
		ListRange(context.Context, UserParams) ([]*User, error)

		// Create persists a new user to the datastore.
		Create(context.Context, *User) error

		// Update persists an updated user to the datastore.
		Update(context.Context, *User) error

		// Delete deletes a user from the datastore.
		Delete(context.Context, *User) error

		// Count returns a count of human and machine users.
		Count(context.Context) (int64, error)

		// CountHuman returns a count of human users.
		CountHuman(context.Context) (int64, error)
	}

	// UserService provides access to user account
	// resources in the remote system (e.g. GitHub).
	UserService interface {
		// Find returns the authenticated user.
		Find(ctx context.Context, access, refresh string) (*User, error)

		// FindLogin returns a user by username.
		FindLogin(ctx context.Context, user *User, login string) (*User, error)
	}
)

// Validate validates the user and returns an error if the
// validation fails.
func (u *User) Validate() error {
	switch {
	case !govalidator.IsByteLength(u.Login, 1, 50):
		return errUsernameLen
	case !govalidator.Matches(u.Login, "^[.a-zA-Z0-9_-]+$"):
		return errUsernameChar
	default:
		return nil
	}
}
