package migrations

import (
	"context"
	"fmt"

	"github.com/jmoiron/sqlx"
	"github.com/stashapp/stash/pkg/logger"
	"github.com/stashapp/stash/pkg/sqlite"
)

func pre48(ctx context.Context, db *sqlx.DB) error {
	logger.Info("Running pre-migration for schema version 48")

	m := schema48PreMigrator{
		migrator: migrator{
			db: db,
		},
	}

	if err := m.validateScrapedItems(ctx); err != nil {
		return err
	}

	if err := m.fixStudioNames(ctx); err != nil {
		return err
	}

	return nil
}

type schema48PreMigrator struct {
	migrator
}

func (m *schema48PreMigrator) validateScrapedItems(ctx context.Context) error {
	var count int

	row := m.db.QueryRowx("SELECT COUNT(*) FROM scraped_items")
	err := row.Scan(&count)
	if err != nil {
		return err
	}

	if count == 0 {
		return nil
	}

	return fmt.Errorf("found %d row(s) in scraped_items table, cannot migrate", count)
}

func (m *schema48PreMigrator) fixStudioNames(ctx context.Context) error {
	// First remove NULL names
	if err := m.withTxn(ctx, func(tx *sqlx.Tx) error {
		_, err := m.db.Exec("UPDATE studios SET name = 'NULL' WHERE name IS NULL")
		return err
	}); err != nil {
		return err
	}

	// Then remove duplicate names

	dupes := make(map[string][]int)

	// collect names
	if err := m.withTxn(ctx, func(tx *sqlx.Tx) error {
		rows, err := m.db.Query("SELECT id, name FROM studios ORDER BY name, id")
		if err != nil {
			return err
		}
		defer rows.Close()

		first := true
		var lastName string

		for rows.Next() {
			var (
				id   int
				name string
			)

			err := rows.Scan(&id, &name)
			if err != nil {
				return err
			}

			if first {
				first = false
				lastName = name
				continue
			}

			if lastName == name {
				dupes[name] = append(dupes[name], id)
			} else {
				lastName = name
			}
		}

		return rows.Err()
	}); err != nil {
		return err
	}

	// rename them
	if err := m.withTxn(ctx, func(tx *sqlx.Tx) error {
		for name, ids := range dupes {
			i := 0
			for _, id := range ids {
				var newName string
				for j := 0; ; j++ {
					i++
					newName = fmt.Sprintf("%s (%d)", name, i)

					var count int

					row := m.db.QueryRowx("SELECT COUNT(*) FROM studios WHERE name = ?", newName)
					err := row.Scan(&count)
					if err != nil {
						return err
					}

					if count == 0 {
						break
					}

					// try up to 100 times to find a unique name
					if j == 100 {
						return fmt.Errorf("cannot make unique studio name for %s", name)
					}
				}

				logger.Infof("Renaming duplicate studio id %d to %s", id, newName)
				_, err := m.db.Exec("UPDATE studios SET name = ? WHERE id = ?", newName, id)
				if err != nil {
					return err
				}
			}
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

func init() {
	sqlite.RegisterPreMigration(48, pre48)
}
