package migrations

import (
	"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
	"github.com/grafana/grafana/pkg/util/xorm"
)

type DummyMigration struct {
	migrator.MigrationBase
}

func (m *DummyMigration) SQL(dialect migrator.Dialect) string {
	return "code migration"
}

func (m *DummyMigration) Exec(sess *xorm.Session, mgrtr *migrator.Migrator) error {
	return nil
}

// FolderUIDMigration is a code migration that populates folder_uid column
type FolderUIDMigration struct {
	migrator.MigrationBase
}

func (m *FolderUIDMigration) SQL(dialect migrator.Dialect) string {
	return "code migration"
}

func (m *FolderUIDMigration) Exec(sess *xorm.Session, mgrtr *migrator.Migrator) error {
	// for dashboards the source of truth is the  dashboard table
	q := `UPDATE dashboard
	SET folder_uid = folder.uid
	FROM dashboard folder
	WHERE dashboard.folder_id = folder.id
	  AND dashboard.is_folder = ?`
	if mgrtr.Dialect.DriverName() == migrator.MySQL {
		q = `UPDATE dashboard AS d
		LEFT JOIN dashboard AS folder ON d.folder_id = folder.id
		SET d.folder_uid = folder.uid
		WHERE d.is_folder = ?`
	}

	r, err := sess.Exec(q, mgrtr.Dialect.BooleanValue(false))
	if err != nil {
		mgrtr.Logger.Error("Failed to migrate dashboard folder_uid for dashboards", "error", err)
		return err
	}
	dashboardRowsAffected, dashboardRowsAffectedErr := r.RowsAffected()
	if dashboardRowsAffectedErr != nil {
		mgrtr.Logger.Error("Failed to get dashboard rows affected", "error", dashboardRowsAffectedErr)
	}

	// for folders the source of truth is the folder table
	// covered by UQE_folder_org_id_uid
	q = `UPDATE dashboard
	SET folder_uid = folder.parent_uid
	FROM folder
	WHERE dashboard.uid = folder.uid AND dashboard.org_id = folder.org_id
	AND dashboard.is_folder = ?`

	// covered by UQE_folder_org_id_uid
	if mgrtr.Dialect.DriverName() == migrator.MySQL {
		q = `UPDATE dashboard
		SET folder_uid = (
		    SELECT folder.parent_uid
		    FROM folder
		    WHERE dashboard.uid = folder.uid AND dashboard.org_id = folder.org_id
		)
		WHERE is_folder = ?`
	}
	r, err = sess.Exec(q, mgrtr.Dialect.BooleanValue(true))
	if err != nil {
		mgrtr.Logger.Error("Failed to migrate dashboard folder_uid for folders", "error", err)
		return err
	}

	folderRowsAffected, folderRowsAffectedErr := r.RowsAffected()
	if folderRowsAffectedErr != nil {
		mgrtr.Logger.Error("Failed to get folder rows affected", "error", folderRowsAffectedErr)
	}

	mgrtr.Logger.Debug("Migrating dashboard data", "dashboards rows", dashboardRowsAffected, "folder rows", folderRowsAffected)
	return nil
}

func AddDashboardFolderMigrations(mg *migrator.Migrator) {
	mg.AddMigration("Add folder_uid for dashboard", migrator.NewAddColumnMigration(migrator.Table{Name: "dashboard"}, &migrator.Column{
		Name: "folder_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: true,
	}))

	mg.AddMigration("Populate dashboard folder_uid column", &FolderUIDMigration{})

	mg.AddMigration("Add unique index for dashboard_org_id_folder_uid_title", &DummyMigration{})

	mg.AddMigration("Delete unique index for dashboard_org_id_folder_id_title", migrator.NewDropIndexMigration(migrator.Table{Name: "dashboard"}, &migrator.Index{
		Cols: []string{"org_id", "folder_id", "title"}, Type: migrator.UniqueIndex,
	}))

	mg.AddMigration("Delete unique index for dashboard_org_id_folder_uid_title", &DummyMigration{})

	// Removed a few lines below
	mg.AddMigration("Add unique index for dashboard_org_id_folder_uid_title_is_folder", migrator.NewAddIndexMigration(migrator.Table{Name: "dashboard"}, &migrator.Index{
		Cols: []string{"org_id", "folder_uid", "title", "is_folder"}, Type: migrator.UniqueIndex,
	}))

	// Temporary index until decommisioning of folder_id in query
	mg.AddMigration("Restore index for dashboard_org_id_folder_id_title", migrator.NewAddIndexMigration(migrator.Table{Name: "dashboard"}, &migrator.Index{
		Cols: []string{"org_id", "folder_id", "title"},
	}))

	mg.AddMigration("Remove unique index for dashboard_org_id_folder_uid_title_is_folder", migrator.NewDropIndexMigration(migrator.Table{Name: "dashboard"}, &migrator.Index{
		Cols: []string{"org_id", "folder_uid", "title", "is_folder"}, Type: migrator.UniqueIndex,
	}))
}
