package views

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/qor5/admin/activity"
	"github.com/qor5/admin/presets"
	"github.com/qor5/admin/presets/actions"
	"github.com/qor5/admin/publish"
	. "github.com/qor5/ui/vuetify"
	"github.com/qor5/web"
	"github.com/qor5/x/i18n"
	"github.com/sunfmin/reflectutils"
	h "github.com/theplant/htmlgo"
	"golang.org/x/text/language"
	"gorm.io/gorm"
)

const I18nPublishKey i18n.ModuleKey = "I18nPublishKey"

func Configure(b *presets.Builder, db *gorm.DB, ab *activity.ActivityBuilder, publisher *publish.Builder, models ...*presets.ModelBuilder) {
	for _, m := range models {
		obj := m.NewModel()
		_ = obj.(presets.SlugEncoder)
		_ = obj.(presets.SlugDecoder)
		if model, ok := obj.(publish.VersionInterface); ok {
			if schedulePublishModel, ok := model.(publish.ScheduleInterface); ok {
				publish.VersionPublishModels[m.Info().URIName()] = reflect.ValueOf(schedulePublishModel).Elem().Interface()
			}

			m.Editing().SidePanelFunc(sidePanel(db, m)).ActionsFunc(versionActionsFunc(m))
			searcher := m.Listing().Searcher
			mb := m
			m.Listing().SearchFunc(func(model interface{}, params *presets.SearchParams, ctx *web.EventContext) (r interface{}, totalCount int, err error) {
				stmt := &gorm.Statement{DB: db}
				stmt.Parse(mb.NewModel())
				tn := stmt.Schema.Table

				var pks []string
				condition := ""
				for _, f := range stmt.Schema.Fields {
					if f.Name == "DeletedAt" {
						condition = "WHERE deleted_at IS NULL"
					}
				}
				for _, f := range stmt.Schema.PrimaryFields {
					if f.Name != "Version" {
						pks = append(pks, f.DBName)
					}
				}
				pkc := strings.Join(pks, ",")
				sql := fmt.Sprintf("(%v,version) IN (SELECT %v, MAX(version) FROM %v %v GROUP BY %v)", pkc, pkc, tn, condition, pkc)

				con := presets.SQLCondition{
					Query: sql,
				}
				params.SQLConditions = append(params.SQLConditions, &con)

				return searcher(model, params, ctx)
			})

			// listing-delete deletes all versions
			{
				// rewrite Delete row menu item to show correct id in prompt message
				m.Listing().RowMenu().RowMenuItem("Delete").ComponentFunc(func(obj interface{}, id string, ctx *web.EventContext) h.HTMLComponent {
					msgr := presets.MustGetMessages(ctx.R)
					if m.Info().Verifier().Do(presets.PermDelete).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil {
						return nil
					}

					promptID := id
					if slugger, ok := obj.(presets.SlugDecoder); ok {
						fvs := []string{}
						for f, v := range slugger.PrimaryColumnValuesBySlug(id) {
							if f == "id" {
								fvs = append([]string{v}, fvs...)
							} else {
								if f != "version" {
									fvs = append(fvs, v)
								}
							}
						}
						promptID = strings.Join(fvs, "_")
					}

					onclick := web.Plaid().
						EventFunc(actions.DeleteConfirmation).
						Query(presets.ParamID, id).
						Query("all_versions", true).
						Query("prompt_id", promptID)
					if presets.IsInDialog(ctx.R.Context()) {
						onclick.URL(ctx.R.RequestURI).
							Query(presets.ParamOverlay, actions.Dialog).
							Query(presets.ParamInDialog, true).
							Query(presets.ParamListingQueries, ctx.Queries().Encode())
					}
					return VListItem(
						VListItemIcon(VIcon("delete")),
						VListItemTitle(h.Text(msgr.Delete)),
					).Attr("@click", onclick.Go())
				})
				// rewrite Deleter to ignore version condition
				m.Editing().DeleteFunc(func(obj interface{}, id string, ctx *web.EventContext) (err error) {
					allVersions := ctx.R.URL.Query().Get("all_versions") == "true"

					wh := db.Model(obj)

					if id != "" {
						if slugger, ok := obj.(presets.SlugDecoder); ok {
							cs := slugger.PrimaryColumnValuesBySlug(id)
							for key, value := range cs {
								if allVersions && key == "version" {
									continue
								}
								wh = wh.Where(fmt.Sprintf("%s = ?", key), value)
							}
						} else {
							wh = wh.Where("id =  ?", id)
						}
					}

					return wh.Delete(obj).Error
				})
			}

			setter := m.Editing().Setter
			m.Editing().SetterFunc(func(obj interface{}, ctx *web.EventContext) {
				if ctx.R.FormValue(presets.ParamID) == "" {
					version := fmt.Sprintf("%s-v01", db.NowFunc().Format("2006-01-02"))
					if err := reflectutils.Set(obj, "Version.Version", version); err != nil {
						return
					}
					if err := reflectutils.Set(obj, "Version.VersionName", version); err != nil {
						return
					}
				}
				if setter != nil {
					setter(obj, ctx)
				}
			})

			m.Listing().Field("Draft Count").ComponentFunc(draftCountFunc(db))
			m.Listing().Field("Online").ComponentFunc(onlineFunc(db))
		} else {
			if schedulePublishModel, ok := obj.(publish.ScheduleInterface); ok {
				publish.NonVersionPublishModels[m.Info().URIName()] = reflect.ValueOf(schedulePublishModel).Elem().Interface()
			}
		}

		if model, ok := obj.(publish.ListInterface); ok {
			if schedulePublishModel, ok := model.(publish.ScheduleInterface); ok {
				publish.ListPublishModels[m.Info().URIName()] = reflect.ValueOf(schedulePublishModel).Elem().Interface()
			}
		}

		registerEventFuncs(db, m, publisher, ab)
	}

	b.FieldDefaults(presets.LIST).
		FieldType(publish.Status{}).
		ComponentFunc(StatusListFunc())
	b.FieldDefaults(presets.WRITE).
		FieldType(publish.Status{}).
		ComponentFunc(StatusEditFunc()).
		SetterFunc(StatusEditSetterFunc)

	b.FieldDefaults(presets.WRITE).
		FieldType(publish.Schedule{}).
		ComponentFunc(ScheduleEditFunc()).
		SetterFunc(ScheduleEditSetterFunc)

	b.I18n().
		RegisterForModule(language.English, I18nPublishKey, Messages_en_US).
		RegisterForModule(language.SimplifiedChinese, I18nPublishKey, Messages_zh_CN).
		RegisterForModule(language.Japanese, I18nPublishKey, Messages_ja_JP)
}