package common

import (
	"time"

	"party.at/party/internal/data"
	"party.at/party/internal/validator"
)

type RegisterUserInput struct {
	ProviderID  int64     `json:"provider_id"`
	Username    string    `json:"username"`
	PhoneNumber string    `json:"phone_number"`
	Country     string    `json:"country"`
	Email       string    `json:"email"`
	Password    string    `json:"password"`
	Name        string    `json:"name"`
	AltName     *string   `json:"alt_name"`
	DateOfBirth time.Time `json:"date_of_birth"`
	Address     string    `json:"address"`
	Role        string    `json:"role"`
}

func (app *Application) RegisterUser(input RegisterUserInput) (*data.User, *data.Token, error) {
	user := &data.User{
		Email:       input.Email,
		PhoneNumber: input.PhoneNumber,
		Country:     input.Country,
		Name:        input.Name,
		AltName:     input.AltName,
		DateOfBirth: input.DateOfBirth,
		Address:     input.Address,
		Activated:   app.Config.Env == "development",
	}

	userIdentity := &data.UserIdentity{
		ProviderID:     input.ProviderID,
		ProviderUserID: input.Username,
	}

	if err := userIdentity.Password.Set(input.Password); err != nil {
		return nil, nil, err
	}

	v := validator.New()
	data.ValidateUser(v, user)
	data.ValidateUserIdentity(v, userIdentity)
	if !v.Valid() {
		return nil, nil, data.ErrValidationFailed.(*data.Error).WithDetails(v.Errors)
	}

	if err := app.Models.Users.ExecuteRegistrationTx(user, userIdentity); err != nil {
		return nil, nil, err
	}

	role := "viewer"
	if app.Config.Env == "development" && input.Role != "" {
		role = input.Role
	}

	if err := app.Models.Roles.AssignToUser(user.ID, role); err != nil {
		return nil, nil, err
	}

	if app.Config.Env == "production" {
		token, err := app.Models.Tokens.New(user.ID, userIdentity.ID, 3*24*time.Hour, data.ScopeActivation)
		if err != nil {
			return nil, nil, err
		}
		app.background(func() {
			templateData := map[string]interface{}{
				"token":  token.Plaintext,
				"userID": user.ID,
			}
			if err := app.Mailer.Send(user.Email, "user_welcome.tmpl", templateData); err != nil {
				app.Logger.PrintError(err, nil)
			}
		})
	}

	authToken, err := app.Models.Tokens.New(user.ID, userIdentity.ID, 24*time.Hour, data.ScopeAuthentication)
	if err != nil {
		return nil, nil, err
	}

	return user, authToken, nil
}

func (app *Application) GetUser(user *data.User, id int64) (*data.User, error) {
	var ret data.User

	roles, err := app.Models.Roles.GetAllForUser(user.ID)
	if err != nil {
		return &ret, err
	}

	target, err := app.Models.Users.Get(id)
	if err != nil {
		return &ret, err
	}

	if user.ID == target.ID {
		return target, nil
	}

	if roles[0].Code == "viewer" {
		return nil, data.ErrNotPermitted
	}

	if roles[0].Code == "contributor" {
		return nil, data.ErrInvalidCredentials
	}

	if roles[0].Code == "member_of_parliament" {
		ret.Email = target.Email
		ret.PhoneNumber = target.PhoneNumber
		ret.Country = target.Country
		ret.Name = target.Name
		ret.AltName = target.AltName
		ret.DateOfBirth = target.DateOfBirth
		ret.Address = target.Address
	}

	if roles[0].Code == "party_leadership" {

	}

	if roles[0].Code == "admin" {
		ret.ID = target.ID
		ret.LastLogin = target.LastLogin
		ret.Activated = target.Activated
		ret.Created = target.Created
		ret.Version = target.Version
	}

	return &ret, nil
}

func (app *Application) ListUsers(filters data.Filters) ([]*data.User, data.Metadata, error) {
	return app.Models.Users.GetAll(filters)
}

func (app *Application) DeleteUser(id int64) error {
	return app.Models.Users.Delete(id)
}

func (app *Application) ActivateUser(tokenPlaintext string) (*data.User, error) {
	user, err := app.Models.Users.GetForToken(data.ScopeActivation, tokenPlaintext)
	if err != nil {
		return nil, err
	}

	user.Activated = true
	if err = app.Models.Users.Update(user); err != nil {
		return nil, err
	}

	if err = app.Models.Tokens.DeleteAllForUser(data.ScopeActivation, user.ID); err != nil {
		return nil, err
	}

	return user, nil
}
