package admin

import (
	"quyun/app/models"
	"quyun/app/requests"
	"quyun/database/conds"
	"quyun/database/fields"
	"quyun/database/schemas/public/model"

	"github.com/gofiber/fiber/v3"
	"github.com/samber/lo"
)

type ListQuery struct {
	Keyword *string `query:"keyword"`
}

// @provider
type posts struct{}

// List posts
//
//	@Router	/admin/posts [get]
//	@Bind	pagination query
//	@Bind	query query
func (ctl *posts) List(ctx fiber.Ctx, pagination *requests.Pagination, query *ListQuery) (*requests.Pager, error) {
	conds := []conds.Cond{
		// conds.Post_NotDeleted(),
		// conds.Post_Status(fields.PostStatusPublished),
		conds.Post_Like(query.Keyword),
	}
	pager, err := models.Posts.List(ctx.Context(), pagination, conds...)
	if err != nil {
		return nil, err
	}

	postIds := lo.Map(pager.Items.([]model.Posts), func(item model.Posts, _ int) int64 {
		return item.ID
	})
	if len(postIds) > 0 {
		postCntMap, err := models.Posts.BoughtStatistics(ctx.Context(), postIds)
		if err != nil {
			return pager, err
		}

		items := lo.Map(pager.Items.([]model.Posts), func(item model.Posts, _ int) PostItem {
			cnt := int64(0)
			if v, ok := postCntMap[item.ID]; ok {
				cnt = v
			}

			return PostItem{Posts: &item, BoughtCount: cnt}
		})

		pager.Items = items
	}
	return pager, err
}

type PostForm struct {
	Title        string            `json:"title"`
	HeadImages   []int64           `json:"head_images"`
	Price        int64             `json:"price"`
	Discount     int16             `json:"discount"`
	Introduction string            `json:"introduction"`
	Medias       []int64           `json:"medias"`
	Status       fields.PostStatus `json:"status"`
	Content      string            `json:"content"`
}

// Create
//
//	@Router	/admin/posts [post]
//	@Bind	form body
func (ctl *posts) Create(ctx fiber.Ctx, form *PostForm) error {
	post := model.Posts{
		Title:       form.Title,
		HeadImages:  fields.ToJson(form.HeadImages),
		Price:       form.Price,
		Discount:    form.Discount,
		Description: form.Introduction,
		Status:      form.Status,
		Content:     form.Content,
		Tags:        fields.Json[[]string]{},
		Assets:      fields.Json[[]fields.MediaAsset]{},
	}

	if form.Medias != nil {
		medias, err := models.Medias.GetByIds(ctx.Context(), form.Medias)
		if err != nil {
			return err
		}
		assets := lo.Map(medias, func(media *model.Medias, _ int) fields.MediaAsset {
			return fields.MediaAsset{
				Type:  media.MimeType,
				Media: media.ID,
				Metas: &media.Metas.Data,
			}
		})
		post.Assets = fields.ToJson(assets)
	}

	if err := models.Posts.Create(ctx.Context(), &post); err != nil {
		return err
	}
	return nil
}

// Update posts
//
//	@Router	/admin/posts/:id [put]
//	@Bind	id path
//	@Bind	form body
func (ctl *posts) Update(ctx fiber.Ctx, id int64, form *PostForm) error {
	oldPost, err := models.Posts.GetByID(ctx.Context(), id)
	if err != nil {
		return err
	}

	post := &model.Posts{
		Title:       form.Title,
		HeadImages:  fields.ToJson(form.HeadImages),
		Price:       form.Price,
		Discount:    form.Discount,
		Description: form.Introduction,
		Status:      form.Status,
		Content:     form.Content,
		Tags:        fields.Json[[]string]{},
		Assets:      fields.Json[[]fields.MediaAsset]{},
		CreatedAt:   oldPost.CreatedAt,
		UpdatedAt:   oldPost.UpdatedAt,
		DeletedAt:   oldPost.DeletedAt,
		Views:       oldPost.Views,
		Likes:       oldPost.Likes,
	}

	if form.Medias != nil {
		medias, err := models.Medias.GetByIds(ctx.Context(), form.Medias)
		if err != nil {
			return err
		}
		assets := lo.Map(medias, func(media *model.Medias, _ int) fields.MediaAsset {
			return fields.MediaAsset{
				Type:  media.MimeType,
				Media: media.ID,
				Metas: &media.Metas.Data,
			}
		})
		post.Assets = fields.ToJson(assets)
	}

	if err := models.Posts.Update(ctx.Context(), id, post); err != nil {
		return err
	}
	return nil
}

// Delete posts
//
//	@Router	/admin/posts/:id [delete]
//	@Bind	id path
func (ctl *posts) Delete(ctx fiber.Ctx, id int64) error {
	post, err := models.Posts.GetByID(ctx.Context(), id)
	if err != nil {
		return err
	}
	if post == nil {
		return fiber.ErrNotFound
	}

	if err := models.Posts.DeleteByID(ctx.Context(), id); err != nil {
		return err
	}
	return nil
}

type PostItem struct {
	*model.Posts
	Medias      []*model.Medias `json:"medias"`
	BoughtCount int64           `json:"bought_count"`
}

// Show posts by id
//
//	@Router	/admin/posts/:id [get]
//	@Bind	id path
func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) {
	post, err := models.Posts.GetByID(ctx.Context(), id)
	if err != nil {
		return nil, err
	}

	medias, err := models.Medias.GetByIds(ctx.Context(), lo.Map(post.Assets.Data, func(asset fields.MediaAsset, _ int) int64 {
		return asset.Media
	}))
	if err != nil {
		return nil, err
	}
	return &PostItem{
		Posts:  post,
		Medias: medias,
	}, nil
}

// SendTo
//
//	@Router	/admin/posts/:id/send-to/:userId [post]
//	@Bind	id path
//	@Bind	userId path
func (ctl *posts) SendTo(ctx fiber.Ctx, id, userId int64) error {
	if _, err := models.Posts.GetByID(ctx.Context(), id); err != nil {
		return err
	}

	if _, err := models.Users.GetByID(ctx.Context(), userId); err != nil {
		return err
	}

	if err := models.Posts.SendTo(ctx.Context(), id, userId); err != nil {
		return err
	}
	return nil
}
