// Copyright 2021 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package migration

import (
	"fmt"
	"reflect"

	"src.techknowlogick.com/xormigrate"
	"xorm.io/xorm"

	"go.woodpecker-ci.org/woodpecker/v2/server/model"
)

// APPEND NEW MIGRATIONS
// they are executed in order and if one fails Xormigrate will try to rollback that specific one and quits
var migrationTasks = []*xormigrate.Migration{
	&legacyToXormigrate,
	&legacy2Xorm,
	&alterTableReposDropFallback,
	&alterTableReposDropAllowDeploysAllowTags,
	&fixPRSecretEventName,
	&alterTableReposDropCounter,
	&dropSenders,
	&alterTableLogUpdateColumnLogDataType,
	&alterTableSecretsAddUserCol,
	&recreateAgentsTable,
	&lowercaseSecretNames,
	&renameBuildsToPipeline,
	&renameColumnsBuildsToPipeline,
	&renameTableProcsToSteps,
	&renameRemoteToForge,
	&renameForgeIDToForgeRemoteID,
	&removeActiveFromUsers,
	&removeInactiveRepos,
	&dropFiles,
	&removeMachineCol,
	&dropOldCols,
	&initLogsEntriesTable,
	&migrateLogs2LogEntries,
	&parentStepsToWorkflows,
	&addOrgs,
	&addOrgID,
	&alterTableTasksUpdateColumnTaskDataType,
	&alterTableConfigUpdateColumnConfigDataType,
	&removePluginOnlyOptionFromSecretsTable,
	&convertToNewPipelineErrorFormat,
	&renameLinkToURL,
	&cleanRegistryPipeline,
}

var allBeans = []any{
	new(model.Agent),
	new(model.Pipeline),
	new(model.PipelineConfig),
	new(model.Config),
	new(model.LogEntry),
	new(model.Perm),
	new(model.Step),
	new(model.Registry),
	new(model.Repo),
	new(model.Secret),
	new(model.Task),
	new(model.User),
	new(model.ServerConfig),
	new(model.Cron),
	new(model.Redirection),
	new(model.Workflow),
	new(model.Org),
}

func Migrate(e *xorm.Engine, allowLong bool) error {
	e.SetDisableGlobalCache(true)

	m := xormigrate.New(e, migrationTasks)
	m.AllowLong(allowLong)
	oldCount, err := e.Table("migrations").Count()
	if oldCount < 1 || err != nil {
		// allow new schema initialization if old migrations table is empty or it does not exist (err != nil)
		// schema initialization will always run if we call `InitSchema`
		m.InitSchema(func(engine *xorm.Engine) error {
			// do nothing on schema init, models are synced in any case below
			return nil
		})
	}

	m.SetLogger(&xormigrateLogger{})

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

	e.SetDisableGlobalCache(false)

	if err := syncAll(e); err != nil {
		return fmt.Errorf("msg: %w", err)
	}

	return nil
}

func syncAll(sess *xorm.Engine) error {
	for _, bean := range allBeans {
		if err := sess.Sync(bean); err != nil {
			return fmt.Errorf("sync error '%s': %w", reflect.TypeOf(bean), err)
		}
	}
	return nil
}
