package core

import (
	"database/sql"
	"errors"
	"fmt"

	"github.com/pocketbase/pocketbase/tools/hook"
	"github.com/pocketbase/pocketbase/tools/router"
)

const CollectionNameSuperusers = "_superusers"

// DefaultInstallerEmail is the default superuser email address
// for the initial autogenerated superuser account.
const DefaultInstallerEmail = "__pbinstaller@example.com"

func (app *BaseApp) registerSuperuserHooks() {
	app.OnRecordDelete(CollectionNameSuperusers).Bind(&hook.Handler[*RecordEvent]{
		Id: "pbSuperusersRecordDelete",
		Func: func(e *RecordEvent) error {
			originalApp := e.App
			txErr := e.App.RunInTransaction(func(txApp App) error {
				e.App = txApp

				total, err := e.App.CountRecords(CollectionNameSuperusers)
				if err != nil {
					return fmt.Errorf("failed to fetch total superusers count: %w", err)
				}

				if total == 1 {
					return router.NewBadRequestError("You can't delete the only existing superuser", nil)
				}

				return e.Next()
			})
			e.App = originalApp

			return txErr
		},
		Priority: -99,
	})

	recordSaveHandler := &hook.Handler[*RecordEvent]{
		Id: "pbSuperusersRecordSaveExec",
		Func: func(e *RecordEvent) error {
			e.Record.SetVerified(true) // always mark superusers as verified

			if err := e.Next(); err != nil {
				return err
			}

			// ensure that the installer superuser is deleted
			if e.Type == ModelEventTypeCreate && e.Record.Email() != DefaultInstallerEmail {
				record, err := app.FindAuthRecordByEmail(CollectionNameSuperusers, DefaultInstallerEmail)
				if errors.Is(err, sql.ErrNoRows) {
					// already deleted
				} else if err != nil {
					e.App.Logger().Warn("Failed to fetch installer superuser", "error", err)
				} else {
					err = e.App.Delete(record)
					if err != nil {
						e.App.Logger().Warn("Failed to delete installer superuser", "error", err)
					}
				}
			}

			return nil
		},
		Priority: -99,
	}
	app.OnRecordCreateExecute(CollectionNameSuperusers).Bind(recordSaveHandler)
	app.OnRecordUpdateExecute(CollectionNameSuperusers).Bind(recordSaveHandler)

	// prevent sending password reset emails to the installer address
	app.OnMailerRecordPasswordResetSend(CollectionNameSuperusers).Bind(&hook.Handler[*MailerRecordEvent]{
		Id: "pbSuperusersInstallerPasswordReset",
		Func: func(e *MailerRecordEvent) error {
			if e.Record.Email() == DefaultInstallerEmail {
				return errors.New("cannot reset the password for the installer superuser")
			}

			return e.Next()
		},
	})

	collectionSaveHandler := &hook.Handler[*CollectionEvent]{
		Id: "pbSuperusersCollectionSaveExec",
		Func: func(e *CollectionEvent) error {
			// don't allow name change even if executed with SaveNoValidate
			e.Collection.Name = CollectionNameSuperusers

			// for now don't allow superusers OAuth2 since we don't want
			// to accidentally create a new superuser by just OAuth2 signin
			e.Collection.OAuth2.Enabled = false
			e.Collection.OAuth2.Providers = nil

			// force password auth
			e.Collection.PasswordAuth.Enabled = true

			// for superusers we don't allow for now standalone OTP auth and always require to be combined with MFA
			if e.Collection.OTP.Enabled {
				e.Collection.MFA.Enabled = true
			}

			return e.Next()
		},
		Priority: 99,
	}
	app.OnCollectionCreateExecute(CollectionNameSuperusers).Bind(collectionSaveHandler)
	app.OnCollectionUpdateExecute(CollectionNameSuperusers).Bind(collectionSaveHandler)
}

// IsSuperuser returns whether the current record is a superuser, aka.
// whether the record is from the _superusers collection.
func (m *Record) IsSuperuser() bool {
	return m.Collection().Name == CollectionNameSuperusers
}
