package services

import (
	"context"
	"os"
	"time"

	"quyun/v2/app/errorx"
	user_dto "quyun/v2/app/http/v1/dto"
	"quyun/v2/app/jobs/args"
	"quyun/v2/app/requests"
	"quyun/v2/database/models"
	"quyun/v2/providers/job"
)

// @provider
type notification struct {
	job *job.Job
}

func (s *notification) List(ctx context.Context, tenantID, userID int64, page int, typeArg string) (*requests.Pager, error) {
	tbl, q := models.NotificationQuery.QueryContext(ctx)
	q = q.Where(tbl.UserID.Eq(userID))
	if tenantID > 0 {
		q = q.Where(tbl.TenantID.Eq(tenantID))
	}

	if typeArg != "" && typeArg != "all" {
		q = q.Where(tbl.Type.Eq(typeArg))
	}

	q = q.Order(tbl.CreatedAt.Desc())

	p := requests.Pagination{Page: int64(page), Limit: 20}
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	list, err := q.Offset(int(p.Offset())).Limit(int(p.Limit)).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	data := make([]user_dto.Notification, len(list))
	for i, v := range list {
		data[i] = user_dto.Notification{
			ID:      v.ID,
			Type:    v.Type,
			Title:   v.Title,
			Content: v.Content,
			Read:    v.IsRead,
			Time:    v.CreatedAt.Format(time.RFC3339),
		}
	}

	return &requests.Pager{
		Pagination: requests.Pagination{Page: p.Page, Limit: p.Limit},
		Total:      total,
		Items:      data,
	}, nil
}

func (s *notification) MarkRead(ctx context.Context, tenantID, userID, id int64) error {
	tbl, q := models.NotificationQuery.QueryContext(ctx)
	q = q.Where(tbl.ID.Eq(id), tbl.UserID.Eq(userID))
	if tenantID > 0 {
		q = q.Where(tbl.TenantID.Eq(tenantID))
	}
	_, err := q.UpdateSimple(tbl.IsRead.Value(true))
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *notification) MarkAllRead(ctx context.Context, tenantID, userID int64) error {
	tbl, q := models.NotificationQuery.QueryContext(ctx)
	q = q.Where(tbl.UserID.Eq(userID), tbl.IsRead.Is(false))
	if tenantID > 0 {
		q = q.Where(tbl.TenantID.Eq(tenantID))
	}
	_, err := q.UpdateSimple(tbl.IsRead.Value(true))
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *notification) Send(ctx context.Context, tenantID, userID int64, typ, title, content string) error {
	arg := args.NotificationArgs{
		TenantID: tenantID,
		UserID:   userID,
		Type:     typ,
		Title:    title,
		Content:  content,
	}
	// 测试环境下同步写入，避免异步任务未启动导致结果不确定。
	if os.Getenv("JOB_INLINE") == "1" {
		n := &models.Notification{
			TenantID: tenantID,
			UserID:   userID,
			Type:     typ,
			Title:    title,
			Content:  content,
			IsRead:   false,
		}
		if err := models.NotificationQuery.WithContext(ctx).Create(n); err != nil {
			return errorx.ErrDatabaseError.WithCause(err)
		}
		return nil
	}
	return s.job.Add(arg)
}
