// Code generated by BobGen psql v0.42.5. DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.

package models

import (
	"context"
	"io"

	"github.com/Gleipnir-Technology/bob"
	"github.com/Gleipnir-Technology/bob/dialect/psql"
	"github.com/Gleipnir-Technology/bob/dialect/psql/dialect"
	"github.com/Gleipnir-Technology/bob/orm"
	"github.com/stephenafamo/scan"
)

var (
	PreloadCount        = getPreloadCount()
	ThenLoadCount       = getThenLoadCount[*dialect.SelectQuery]()
	InsertThenLoadCount = getThenLoadCount[*dialect.InsertQuery]()
)

type preloadCounts struct {
	Address              addressCountPreloader
	ArcgisAccount        arcgisAccountCountPreloader
	ArcgisLayer          arcgisLayerCountPreloader
	ArcgisLayerField     arcgisLayerFieldCountPreloader
	ArcgisServiceFeature arcgisServiceFeatureCountPreloader
	ArcgisUser           arcgisuserCountPreloader
	CommsEmailContact    commsEmailContactCountPreloader
	CommsEmailTemplate   commsEmailTemplateCountPreloader
	CommsPhone           commsPhoneCountPreloader
	FileuploadCSV        fileuploadCSVCountPreloader
	FileuploadFile       fileuploadFileCountPreloader
	NoteAudio            noteAudioCountPreloader
	NoteImage            noteImageCountPreloader
	Organization         organizationCountPreloader
	Parcel               parcelCountPreloader
	PublicreportImage    publicreportImageCountPreloader
	PublicreportNuisance publicreportNuisanceCountPreloader
	PublicreportPool     publicreportPoolCountPreloader
	PublicreportQuick    publicreportQuickCountPreloader
	Site                 siteCountPreloader
	User                 userCountPreloader
}

func getPreloadCount() preloadCounts {
	return preloadCounts{
		Address:              buildAddressCountPreloader(),
		ArcgisAccount:        buildArcgisAccountCountPreloader(),
		ArcgisLayer:          buildArcgisLayerCountPreloader(),
		ArcgisLayerField:     buildArcgisLayerFieldCountPreloader(),
		ArcgisServiceFeature: buildArcgisServiceFeatureCountPreloader(),
		ArcgisUser:           buildArcgisUserCountPreloader(),
		CommsEmailContact:    buildCommsEmailContactCountPreloader(),
		CommsEmailTemplate:   buildCommsEmailTemplateCountPreloader(),
		CommsPhone:           buildCommsPhoneCountPreloader(),
		FileuploadCSV:        buildFileuploadCSVCountPreloader(),
		FileuploadFile:       buildFileuploadFileCountPreloader(),
		NoteAudio:            buildNoteAudioCountPreloader(),
		NoteImage:            buildNoteImageCountPreloader(),
		Organization:         buildOrganizationCountPreloader(),
		Parcel:               buildParcelCountPreloader(),
		PublicreportImage:    buildPublicreportImageCountPreloader(),
		PublicreportNuisance: buildPublicreportNuisanceCountPreloader(),
		PublicreportPool:     buildPublicreportPoolCountPreloader(),
		PublicreportQuick:    buildPublicreportQuickCountPreloader(),
		Site:                 buildSiteCountPreloader(),
		User:                 buildUserCountPreloader(),
	}
}

type thenLoadCounts[Q orm.Loadable] struct {
	Address              addressCountThenLoader[Q]
	ArcgisAccount        arcgisAccountCountThenLoader[Q]
	ArcgisLayer          arcgisLayerCountThenLoader[Q]
	ArcgisLayerField     arcgisLayerFieldCountThenLoader[Q]
	ArcgisServiceFeature arcgisServiceFeatureCountThenLoader[Q]
	ArcgisUser           arcgisuserCountThenLoader[Q]
	CommsEmailContact    commsEmailContactCountThenLoader[Q]
	CommsEmailTemplate   commsEmailTemplateCountThenLoader[Q]
	CommsPhone           commsPhoneCountThenLoader[Q]
	FileuploadCSV        fileuploadCSVCountThenLoader[Q]
	FileuploadFile       fileuploadFileCountThenLoader[Q]
	NoteAudio            noteAudioCountThenLoader[Q]
	NoteImage            noteImageCountThenLoader[Q]
	Organization         organizationCountThenLoader[Q]
	Parcel               parcelCountThenLoader[Q]
	PublicreportImage    publicreportImageCountThenLoader[Q]
	PublicreportNuisance publicreportNuisanceCountThenLoader[Q]
	PublicreportPool     publicreportPoolCountThenLoader[Q]
	PublicreportQuick    publicreportQuickCountThenLoader[Q]
	Site                 siteCountThenLoader[Q]
	User                 userCountThenLoader[Q]
}

func getThenLoadCount[Q orm.Loadable]() thenLoadCounts[Q] {
	return thenLoadCounts[Q]{
		Address:              buildAddressCountThenLoader[Q](),
		ArcgisAccount:        buildArcgisAccountCountThenLoader[Q](),
		ArcgisLayer:          buildArcgisLayerCountThenLoader[Q](),
		ArcgisLayerField:     buildArcgisLayerFieldCountThenLoader[Q](),
		ArcgisServiceFeature: buildArcgisServiceFeatureCountThenLoader[Q](),
		ArcgisUser:           buildArcgisUserCountThenLoader[Q](),
		CommsEmailContact:    buildCommsEmailContactCountThenLoader[Q](),
		CommsEmailTemplate:   buildCommsEmailTemplateCountThenLoader[Q](),
		CommsPhone:           buildCommsPhoneCountThenLoader[Q](),
		FileuploadCSV:        buildFileuploadCSVCountThenLoader[Q](),
		FileuploadFile:       buildFileuploadFileCountThenLoader[Q](),
		NoteAudio:            buildNoteAudioCountThenLoader[Q](),
		NoteImage:            buildNoteImageCountThenLoader[Q](),
		Organization:         buildOrganizationCountThenLoader[Q](),
		Parcel:               buildParcelCountThenLoader[Q](),
		PublicreportImage:    buildPublicreportImageCountThenLoader[Q](),
		PublicreportNuisance: buildPublicreportNuisanceCountThenLoader[Q](),
		PublicreportPool:     buildPublicreportPoolCountThenLoader[Q](),
		PublicreportQuick:    buildPublicreportQuickCountThenLoader[Q](),
		Site:                 buildSiteCountThenLoader[Q](),
		User:                 buildUserCountThenLoader[Q](),
	}
}

func countThenLoadBuilder[Q orm.Loadable, T any](name string, f func(context.Context, bob.Executor, T, ...bob.Mod[*dialect.SelectQuery]) error) func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] {
	return func(queryMods ...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] {
		return func(ctx context.Context, exec bob.Executor, retrieved any) error {
			loader, isLoader := retrieved.(T)
			if !isLoader {
				return nil // silently skip if not the right type
			}

			return f(ctx, exec, loader, queryMods...)
		}
	}
}

// countPreloadable is an interface for models that can have counts preloaded
type countPreloadable interface {
	PreloadCount(name string, count int64) error
}

// countPreloadMod is used to add a count subquery to the SELECT
type countPreloadMod[T countPreloadable] struct {
	name      string
	countExpr func(from string) bob.Expression
}

// Apply implements bob.Mod
func (c countPreloadMod[T]) Apply(q *dialect.SelectQuery) {
	c.applyCount(q, "")
}

// applyCount adds the count subquery to the query
func (c countPreloadMod[T]) applyCount(q *dialect.SelectQuery, parent string) {
	countCol := c.countExpr(parent)
	q.AppendPreloadSelect(aliasedExpr{expr: countCol, alias: "__count_" + c.name})
}

// aliasedExpr wraps an expression with an alias
type aliasedExpr struct {
	expr  bob.Expression
	alias string
}

func (a aliasedExpr) WriteSQL(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
	args, err := a.expr.WriteSQL(ctx, w, d, start)
	if err != nil {
		return nil, err
	}
	w.WriteString(" AS ")
	d.WriteQuoted(w, a.alias)
	return args, nil
}

// countPreloader returns a Preloader that adds a count subquery
func countPreloader[T countPreloadable](name string, countExpr func(from string) bob.Expression) psql.Preloader {
	return func(parent string) (bob.Mod[*dialect.SelectQuery], scan.MapperMod, []bob.Loader) {
		m := countPreloadMod[T]{
			name:      name,
			countExpr: countExpr,
		}

		queryMod := bob.ModFunc[*dialect.SelectQuery](func(q *dialect.SelectQuery) {
			m.applyCount(q, parent)
		})

		mapperMod := func(ctx context.Context, cols []string) (scan.BeforeFunc, scan.AfterMod) {
			// Find the count column
			countColName := "__count_" + name
			colIndex := -1
			for i, col := range cols {
				if col == countColName {
					colIndex = i
					break
				}
			}

			return func(r *scan.Row) (any, error) {
					if colIndex >= 0 {
						var count *int64
						r.ScheduleScanByIndex(colIndex, &count)
						return &count, nil
					}
					return nil, nil
				}, func(link, retrieved any) error {
					if link == nil {
						return nil
					}
					countPtr, ok := link.(**int64)
					if !ok || countPtr == nil || *countPtr == nil {
						return nil
					}

					loader, isLoader := retrieved.(countPreloadable)
					if !isLoader {
						return nil
					}

					return loader.PreloadCount(name, **countPtr)
				}
		}

		return queryMod, mapperMod, nil
	}
}
