package services

import (
	"context"
	"errors"
	"strings"
	"time"

	"quyun/v2/app/errorx"
	tenant_dto "quyun/v2/app/http/v1/dto"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"

	"github.com/google/uuid"
	"go.ipao.vip/gen/types"
	"gorm.io/gorm"
)

func (s *tenant) ApplyJoin(ctx context.Context, tenantID, userID int64, form *tenant_dto.TenantJoinApplyForm) error {
	if userID == 0 {
		return errorx.ErrUnauthorized
	}
	if tenantID == 0 {
		return errorx.ErrRecordNotFound.WithMsg("租户不存在")
	}

	// 校验租户可加入状态。
	tblTenant, qTenant := models.TenantQuery.QueryContext(ctx)
	tenant, err := qTenant.Where(tblTenant.ID.Eq(tenantID)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errorx.ErrRecordNotFound.WithMsg("租户不存在")
		}
		return errorx.ErrDatabaseError.WithCause(err)
	}
	if tenant.Status != consts.TenantStatusVerified {
		return errorx.ErrBadRequest.WithMsg("租户暂不可加入")
	}
	if tenant.UserID == userID {
		return errorx.ErrBadRequest.WithMsg("您已是租户管理员")
	}

	// 已是成员则不允许重复申请。
	tblMember, qMember := models.TenantUserQuery.QueryContext(ctx)
	exists, err := qMember.Where(tblMember.TenantID.Eq(tenantID), tblMember.UserID.Eq(userID)).Exists()
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	if exists {
		return errorx.ErrBadRequest.WithMsg("您已是租户成员")
	}

	// 防止重复提交同一租户的待审核申请。
	tblReq, qReq := models.TenantJoinRequestQuery.QueryContext(ctx)
	pendingExists, err := qReq.Where(
		tblReq.TenantID.Eq(tenantID),
		tblReq.UserID.Eq(userID),
		tblReq.Status.Eq(string(consts.TenantJoinRequestStatusPending)),
	).Exists()
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	if pendingExists {
		return errorx.ErrBadRequest.WithMsg("已提交申请")
	}

	reason := ""
	if form != nil {
		reason = strings.TrimSpace(form.Reason)
	}
	if reason == "" {
		reason = "申请加入"
	}

	req := &models.TenantJoinRequest{
		TenantID: tenantID,
		UserID:   userID,
		Status:   string(consts.TenantJoinRequestStatusPending),
		Reason:   reason,
	}
	if err := qReq.Create(req); err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *tenant) CancelJoin(ctx context.Context, tenantID, userID int64) error {
	if userID == 0 {
		return errorx.ErrUnauthorized
	}
	if tenantID == 0 {
		return errorx.ErrRecordNotFound.WithMsg("租户不存在")
	}

	tblReq, qReq := models.TenantJoinRequestQuery.QueryContext(ctx)
	req, err := qReq.Where(
		tblReq.TenantID.Eq(tenantID),
		tblReq.UserID.Eq(userID),
		tblReq.Status.Eq(string(consts.TenantJoinRequestStatusPending)),
	).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errorx.ErrRecordNotFound.WithMsg("申请不存在")
		}
		return errorx.ErrDatabaseError.WithCause(err)
	}

	if _, err := qReq.Where(tblReq.ID.Eq(req.ID)).Delete(); err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *tenant) ReviewJoin(
	ctx context.Context,
	tenantID,
	operatorID,
	requestID int64,
	form *tenant_dto.TenantJoinReviewForm,
) error {
	if tenantID == 0 {
		return errorx.ErrRecordNotFound.WithMsg("租户不存在")
	}
	if form == nil {
		return errorx.ErrBadRequest.WithMsg("审核参数不能为空")
	}
	action := strings.ToLower(strings.TrimSpace(form.Action))
	if action != "approve" && action != "reject" {
		return errorx.ErrBadRequest.WithMsg("审核动作无效")
	}

	// 校验操作者为租户管理员或租户主账号。
	if _, err := s.ensureTenantAdmin(ctx, tenantID, operatorID); err != nil {
		return err
	}

	tblReq, qReq := models.TenantJoinRequestQuery.QueryContext(ctx)
	req, err := qReq.Where(tblReq.ID.Eq(requestID)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errorx.ErrRecordNotFound.WithMsg("申请不存在")
		}
		return errorx.ErrDatabaseError.WithCause(err)
	}
	if req.TenantID != tenantID {
		return errorx.ErrForbidden.WithMsg("租户不匹配")
	}
	if req.Status != string(consts.TenantJoinRequestStatusPending) {
		return errorx.ErrBadRequest.WithMsg("申请已处理")
	}

	reason := strings.TrimSpace(form.Reason)
	now := time.Now()

	if action == "reject" {
		_, err = qReq.Where(
			tblReq.ID.Eq(req.ID),
			tblReq.Status.Eq(string(consts.TenantJoinRequestStatusPending)),
		).UpdateSimple(
			tblReq.Status.Value(string(consts.TenantJoinRequestStatusRejected)),
			tblReq.DecidedAt.Value(now),
			tblReq.DecidedOperatorUserID.Value(operatorID),
			tblReq.DecidedReason.Value(reason),
		)
		if err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}
		return nil
	}

	// 审核通过需在事务内写入成员并更新申请状态。
	return models.Q.Transaction(func(tx *models.Query) error {
		tblMember, qMember := tx.TenantUser.QueryContext(ctx)
		exists, err := qMember.Where(
			tblMember.TenantID.Eq(tenantID),
			tblMember.UserID.Eq(req.UserID),
		).Exists()
		if err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}
		if exists {
			return errorx.ErrBadRequest.WithMsg("用户已是成员")
		}

		member := &models.TenantUser{
			TenantID: tenantID,
			UserID:   req.UserID,
			Role:     types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
			Status:   consts.UserStatusVerified,
		}
		if err := qMember.Create(member); err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}

		tblReqTx, qReqTx := tx.TenantJoinRequest.QueryContext(ctx)
		_, err = qReqTx.Where(
			tblReqTx.ID.Eq(req.ID),
			tblReqTx.Status.Eq(string(consts.TenantJoinRequestStatusPending)),
		).UpdateSimple(
			tblReqTx.Status.Value(string(consts.TenantJoinRequestStatusApproved)),
			tblReqTx.DecidedAt.Value(now),
			tblReqTx.DecidedOperatorUserID.Value(operatorID),
			tblReqTx.DecidedReason.Value(reason),
		)
		if err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}
		return nil
	})
}

func (s *tenant) CreateInvite(
	ctx context.Context,
	tenantID,
	operatorID int64,
	form *tenant_dto.TenantInviteCreateForm,
) (*tenant_dto.TenantInviteItem, error) {
	if tenantID == 0 {
		return nil, errorx.ErrRecordNotFound.WithMsg("租户不存在")
	}
	if form == nil {
		return nil, errorx.ErrBadRequest.WithMsg("邀请参数不能为空")
	}

	// 校验操作者权限。
	if _, err := s.ensureTenantAdmin(ctx, tenantID, operatorID); err != nil {
		return nil, err
	}

	maxUses := form.MaxUses
	if maxUses <= 0 {
		maxUses = 1
	}
	expiresAt, err := s.normalizeInviteExpiry(form)
	if err != nil {
		return nil, err
	}
	remark := strings.TrimSpace(form.Remark)
	if remark == "" {
		remark = "成员邀请"
	}

	code, err := s.newInviteCode(ctx)
	if err != nil {
		return nil, err
	}

	invite := &models.TenantInvite{
		TenantID:  tenantID,
		UserID:    operatorID,
		Code:      code,
		Status:    string(consts.TenantInviteStatusActive),
		MaxUses:   maxUses,
		UsedCount: 0,
		ExpiresAt: expiresAt,
		Remark:    remark,
	}
	if err := models.TenantInviteQuery.WithContext(ctx).Create(invite); err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	return s.toTenantInviteItem(invite), nil
}

func (s *tenant) AcceptInvite(ctx context.Context, tenantID, userID int64, form *tenant_dto.TenantInviteAcceptForm) error {
	if userID == 0 {
		return errorx.ErrUnauthorized
	}
	if tenantID == 0 {
		return errorx.ErrRecordNotFound.WithMsg("租户不存在")
	}
	if form == nil || strings.TrimSpace(form.Code) == "" {
		return errorx.ErrBadRequest.WithMsg("邀请码不能为空")
	}

	code := strings.TrimSpace(form.Code)
	now := time.Now()

	// 邀请校验 + 成员入库需要事务保证一致性。
	return models.Q.Transaction(func(tx *models.Query) error {
		tblInvite, qInvite := tx.TenantInvite.QueryContext(ctx)
		invite, err := qInvite.Where(
			tblInvite.TenantID.Eq(tenantID),
			tblInvite.Code.Eq(code),
		).First()
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errorx.ErrRecordNotFound.WithMsg("邀请码无效")
			}
			return errorx.ErrDatabaseError.WithCause(err)
		}

		if invite.Status != string(consts.TenantInviteStatusActive) {
			return errorx.ErrBadRequest.WithMsg("邀请码不可用")
		}
		if !invite.ExpiresAt.IsZero() && invite.ExpiresAt.Before(now) {
			_, err = qInvite.Where(tblInvite.ID.Eq(invite.ID)).UpdateSimple(
				tblInvite.Status.Value(string(consts.TenantInviteStatusExpired)),
			)
			if err != nil {
				return errorx.ErrDatabaseError.WithCause(err)
			}
			return errorx.ErrBadRequest.WithMsg("邀请码已过期")
		}
		if invite.UsedCount >= invite.MaxUses {
			return errorx.ErrBadRequest.WithMsg("邀请码已用尽")
		}

		tblMember, qMember := tx.TenantUser.QueryContext(ctx)
		exists, err := qMember.Where(
			tblMember.TenantID.Eq(tenantID),
			tblMember.UserID.Eq(userID),
		).Exists()
		if err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}
		if exists {
			return errorx.ErrBadRequest.WithMsg("您已是租户成员")
		}

		member := &models.TenantUser{
			TenantID: tenantID,
			UserID:   userID,
			Role:     types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
			Status:   consts.UserStatusVerified,
		}
		if err := qMember.Create(member); err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}

		_, err = qInvite.Where(tblInvite.ID.Eq(invite.ID)).UpdateSimple(
			tblInvite.UsedCount.Value(invite.UsedCount + 1),
		)
		if err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}
		return nil
	})
}

func (s *tenant) ensureTenantAdmin(ctx context.Context, tenantID, userID int64) (*models.Tenant, error) {
	if userID == 0 {
		return nil, errorx.ErrUnauthorized
	}

	tblTenant, qTenant := models.TenantQuery.QueryContext(ctx)
	tenant, err := qTenant.Where(tblTenant.ID.Eq(tenantID)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errorx.ErrRecordNotFound.WithMsg("租户不存在")
		}
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	if tenant.UserID == userID {
		return tenant, nil
	}

	// 非主账号需校验租户管理员角色。
	tblMember, qMember := models.TenantUserQuery.QueryContext(ctx)
	exists, err := qMember.Where(
		tblMember.TenantID.Eq(tenantID),
		tblMember.UserID.Eq(userID),
		tblMember.Status.Eq(consts.UserStatusVerified),
		tblMember.Role.Contains(types.Array[consts.TenantUserRole]{consts.TenantUserRoleTenantAdmin}),
	).Exists()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	if !exists {
		return nil, errorx.ErrPermissionDenied.WithMsg("无权限操作该租户")
	}
	return tenant, nil
}

func (s *tenant) normalizeInviteExpiry(form *tenant_dto.TenantInviteCreateForm) (time.Time, error) {
	if form == nil || form.ExpiresAt == nil || strings.TrimSpace(*form.ExpiresAt) == "" {
		return time.Now().Add(7 * 24 * time.Hour), nil
	}
	raw := strings.TrimSpace(*form.ExpiresAt)
	expireAt, err := time.Parse(time.RFC3339, raw)
	if err != nil {
		return time.Time{}, errorx.ErrBadRequest.WithMsg("过期时间格式错误")
	}
	if expireAt.Before(time.Now()) {
		return time.Time{}, errorx.ErrBadRequest.WithMsg("过期时间不能早于当前时间")
	}
	return expireAt, nil
}

func (s *tenant) newInviteCode(ctx context.Context) (string, error) {
	for i := 0; i < 5; i++ {
		code := strings.ReplaceAll(uuid.NewString(), "-", "")
		exists, err := models.TenantInviteQuery.WithContext(ctx).
			Where(models.TenantInviteQuery.Code.Eq(code)).
			Exists()
		if err != nil {
			return "", errorx.ErrDatabaseError.WithCause(err)
		}
		if !exists {
			return code, nil
		}
	}
	return "", errorx.ErrInternalError.WithMsg("生成邀请码失败")
}

func (s *tenant) toTenantInviteItem(invite *models.TenantInvite) *tenant_dto.TenantInviteItem {
	if invite == nil {
		return nil
	}
	expiresAt := ""
	if !invite.ExpiresAt.IsZero() {
		expiresAt = invite.ExpiresAt.Format(time.RFC3339)
	}
	createdAt := ""
	if !invite.CreatedAt.IsZero() {
		createdAt = invite.CreatedAt.Format(time.RFC3339)
	}
	return &tenant_dto.TenantInviteItem{
		ID:        invite.ID,
		Code:      invite.Code,
		Status:    invite.Status,
		MaxUses:   invite.MaxUses,
		UsedCount: invite.UsedCount,
		ExpiresAt: expiresAt,
		CreatedAt: createdAt,
		Remark:    invite.Remark,
	}
}
