/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 schema

import (
	"context"
	"strings"
	"time"

	"github.com/apache/answer/internal/base/constant"
	"github.com/apache/answer/internal/base/handler"
	"github.com/apache/answer/internal/base/reason"
	"github.com/apache/answer/internal/base/translator"
	"github.com/apache/answer/internal/base/validator"
	"github.com/apache/answer/internal/entity"
	"github.com/segmentfault/pacman/errors"
)

// UpdateUserStatusReq update user request
type UpdateUserStatusReq struct {
	UserID           string `validate:"required" json:"user_id"`
	Status           string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"`
	SuspendDuration  string `validate:"omitempty,oneof=24h 48h 72h 7d 14d 1m 2m 3m 6m 1y forever" json:"suspend_duration"`
	RemoveAllContent bool   `validate:"omitempty" json:"remove_all_content"`
	LoginUserID      string `json:"-"`
}

func (r *UpdateUserStatusReq) IsNormal() bool    { return r.Status == constant.UserNormal }
func (r *UpdateUserStatusReq) IsSuspended() bool { return r.Status == constant.UserSuspended }
func (r *UpdateUserStatusReq) IsDeleted() bool   { return r.Status == constant.UserDeleted }
func (r *UpdateUserStatusReq) IsInactive() bool  { return r.Status == constant.UserInactive }

// GetSuspendedUntil calculates the suspended until time based on duration
func (r *UpdateUserStatusReq) GetSuspendedUntil() time.Time {
	if !r.IsSuspended() || r.SuspendDuration == "" || r.SuspendDuration == "forever" {
		return entity.PermanentSuspensionTime // permanent suspension
	}

	now := time.Now()
	switch r.SuspendDuration {
	case "24h":
		return now.Add(24 * time.Hour)
	case "48h":
		return now.Add(48 * time.Hour)
	case "72h":
		return now.Add(72 * time.Hour)
	case "7d":
		return now.Add(7 * 24 * time.Hour)
	case "14d":
		return now.Add(14 * 24 * time.Hour)
	case "1m":
		return now.AddDate(0, 1, 0)
	case "2m":
		return now.AddDate(0, 2, 0)
	case "3m":
		return now.AddDate(0, 3, 0)
	case "6m":
		return now.AddDate(0, 6, 0)
	case "1y":
		return now.AddDate(1, 0, 0)
	default:
		return entity.PermanentSuspensionTime // fallback to permanent
	}
}

// GetUserPageReq get user list page request
type GetUserPageReq struct {
	// page
	Page int `validate:"omitempty,min=1" form:"page"`
	// page size
	PageSize int `validate:"omitempty,min=1" form:"page_size"`
	// email
	Query string `validate:"omitempty,gt=0,lte=100" form:"query"`
	// user status
	Status string `validate:"omitempty,oneof=normal suspended deleted inactive" form:"status"`
	// staff, if staff is true means query admin or moderator
	Staff bool `validate:"omitempty" form:"staff"`
}

func (r *GetUserPageReq) IsSuspended() bool { return r.Status == constant.UserSuspended }
func (r *GetUserPageReq) IsDeleted() bool   { return r.Status == constant.UserDeleted }
func (r *GetUserPageReq) IsInactive() bool  { return r.Status == constant.UserInactive }

// GetUserPageResp get user response
type GetUserPageResp struct {
	// user id
	UserID string `json:"user_id"`
	// create time
	CreatedAt int64 `json:"created_at"`
	// delete time
	DeletedAt int64 `json:"deleted_at"`
	// suspended time
	SuspendedAt int64 `json:"suspended_at"`
	// suspended until time
	SuspendedUntil int64 `json:"suspended_until"`
	// username
	Username string `json:"username"`
	// email
	EMail string `json:"e_mail"`
	// rank
	Rank int `json:"rank"`
	// user status(normal,suspended,deleted,inactive)
	Status string `json:"status"`
	// display name
	DisplayName string `json:"display_name"`
	// avatar
	Avatar string `json:"avatar"`
	// role id
	RoleID int `json:"role_id"`
	// role name
	RoleName string `json:"role_name"`
}

// GetUserInfoReq get user request
type GetUserInfoReq struct {
	UserID string `validate:"required" json:"user_id"`
}

// GetUserInfoResp get user response
type GetUserInfoResp struct {
	// suspended until
	SuspendedUntil time.Time `json:"suspended_until"`
}

// UpdateUserRoleReq update user role request
type UpdateUserRoleReq struct {
	// user id
	UserID string `validate:"required" json:"user_id"`
	// role id
	RoleID int `validate:"required" json:"role_id"`
	// login user id
	LoginUserID string `json:"-"`
}

// EditUserProfileReq edit user profile request
type EditUserProfileReq struct {
	UserID      string `validate:"required" json:"user_id"`
	DisplayName string `validate:"required,gte=2,lte=30" json:"display_name"`
	Username    string `validate:"omitempty,gte=2,lte=30" json:"username"`
	Email       string `validate:"required,email,gt=0,lte=500" json:"email"`
	LoginUserID string `json:"-"`
	IsAdmin     bool   `json:"-"`
}

// AddUserReq add user request
type AddUserReq struct {
	DisplayName string `validate:"required,gte=2,lte=30" json:"display_name"`
	Email       string `validate:"required,email,gt=0,lte=500" json:"email"`
	Password    string `validate:"required,gte=8,lte=32" json:"password"`
	LoginUserID string `json:"-"`
}

// AddUsersReq add users request
type AddUsersReq struct {
	// users info line by line
	UsersStr string        `json:"users"`
	Users    []*AddUserReq `json:"-"`
}

// DeletePermanentlyReq delete permanently request
type DeletePermanentlyReq struct {
	Type string `validate:"required,oneof=users questions answers" json:"type"`
}

type AddUsersErrorData struct {
	// optional. error field name.
	Field string `json:"field"`
	// must. error line number.
	Line int `json:"line"`
	// must. error content.
	Content string `json:"content"`
	// optional. error message.
	ExtraMessage string `json:"extra_message"`
}

func (e *AddUsersErrorData) GetErrField(ctx context.Context) (errFields []*validator.FormErrorField) {
	return append([]*validator.FormErrorField{}, &validator.FormErrorField{
		ErrorField: "users",
		ErrorMsg:   translator.TrWithData(handler.GetLangByCtx(ctx), reason.AddBulkUsersFormatError, e),
	})
}

func (req *AddUsersReq) ParseUsers(ctx context.Context) (errFields []*validator.FormErrorField, err error) {
	req.UsersStr = strings.TrimSpace(req.UsersStr)
	lines := strings.Split(req.UsersStr, "\n")
	req.Users = make([]*AddUserReq, 0)
	for i, line := range lines {
		arr := strings.Split(line, ",")
		if len(arr) != 3 {
			errFields = append([]*validator.FormErrorField{}, &validator.FormErrorField{
				ErrorField: "users",
				ErrorMsg: translator.TrWithData(handler.GetLangByCtx(ctx), reason.AddBulkUsersFormatError,
					&AddUsersErrorData{
						Line:    i + 1,
						Content: line,
					}),
			})
			return errFields, errors.BadRequest(reason.RequestFormatError)
		}
		req.Users = append(req.Users, &AddUserReq{
			DisplayName: strings.TrimSpace(arr[0]),
			Email:       strings.TrimSpace(arr[1]),
			Password:    strings.TrimSpace(arr[2]),
		})
	}

	// check users amount
	if len(req.Users) == 0 || len(req.Users) > constant.DefaultBulkUser {
		errFields = append([]*validator.FormErrorField{}, &validator.FormErrorField{
			ErrorField: "users",
			ErrorMsg: translator.TrWithData(handler.GetLangByCtx(ctx), reason.AddBulkUsersAmountError,
				map[string]int{
					"MaxAmount": constant.DefaultBulkUser,
				}),
		})
		return errFields, errors.BadRequest(reason.RequestFormatError)
	}
	return nil, nil
}

// UpdateUserPasswordReq update user password request
type UpdateUserPasswordReq struct {
	UserID      string `validate:"required" json:"user_id"`
	Password    string `validate:"required,gte=8,lte=32" json:"password"`
	LoginUserID string `json:"-"`
}

// GetUserActivationReq get user activation
type GetUserActivationReq struct {
	UserID string `validate:"required" form:"user_id"`
}

// GetUserActivationResp get user activation
type GetUserActivationResp struct {
	ActivationURL string `json:"activation_url"`
}

// SendUserActivationReq send user activation
type SendUserActivationReq struct {
	UserID string `validate:"required" json:"user_id"`
}
