package migratecmd

import (
	"database/sql"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pocketbase/dbx"
	"github.com/pocketbase/pocketbase/core"
)

// automigrateOnCollectionChange handles the automigration snapshot
// generation on collection change request event (create/update/delete).
func (p *plugin) automigrateOnCollectionChange(e *core.CollectionRequestEvent) error {
	var err error
	var old *core.Collection
	if !e.Collection.IsNew() {
		old, err = e.App.FindCollectionByNameOrId(e.Collection.Id)
		if err != nil {
			return err
		}
	}

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

	new, err := p.app.FindCollectionByNameOrId(e.Collection.Id)
	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		return err
	}

	// for now exclude OAuth2 configs from the migration
	if old != nil && old.IsAuth() {
		old.OAuth2.Providers = nil
	}
	if new != nil && new.IsAuth() {
		new.OAuth2.Providers = nil
	}

	var template string
	var templateErr error
	if p.config.TemplateLang == TemplateLangJS {
		template, templateErr = p.jsDiffTemplate(new, old)
	} else {
		template, templateErr = p.goDiffTemplate(new, old)
	}
	if templateErr != nil {
		if errors.Is(templateErr, ErrEmptyTemplate) {
			return nil // no changes
		}
		return fmt.Errorf("failed to resolve template: %w", templateErr)
	}

	var action string
	switch {
	case new == nil:
		action = "deleted_" + normalizeCollectionName(old.Name)
	case old == nil:
		action = "created_" + normalizeCollectionName(new.Name)
	default:
		action = "updated_" + normalizeCollectionName(old.Name)
	}

	name := fmt.Sprintf("%d_%s.%s", time.Now().Unix(), action, p.config.TemplateLang)
	filePath := filepath.Join(p.config.Dir, name)

	return p.app.RunInTransaction(func(txApp core.App) error {
		// insert the migration entry
		_, err := txApp.DB().Insert(core.DefaultMigrationsTable, dbx.Params{
			"file": name,
			// use microseconds for more granular applied time in case
			// multiple collection changes happens at the ~exact time
			"applied": time.Now().UnixMicro(),
		}).Execute()
		if err != nil {
			return err
		}

		// ensure that the local migrations dir exist
		if err := os.MkdirAll(p.config.Dir, os.ModePerm); err != nil {
			return fmt.Errorf("failed to create migration dir: %w", err)
		}

		if err := os.WriteFile(filePath, []byte(template), 0644); err != nil {
			return fmt.Errorf("failed to save automigrate file: %w", err)
		}

		return nil
	})
}

func normalizeCollectionName(name string) string {
	// adds an extra "_" suffix to the name in case the collection ends
	// with "test" to prevent accidentally resulting in "_test.go"/"_test.js" files
	if strings.HasSuffix(strings.ToLower(name), "test") {
		name += "_"
	}

	return name
}