// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_21

import (
	repo_model "forgejo.org/models/repo"
	"forgejo.org/modules/setting"

	"xorm.io/xorm"
)

func AddRemoteAddressToMirrors(x *xorm.Engine) error {
	type Mirror struct {
		RemoteAddress string `xorm:"VARCHAR(2048)"`
	}

	type PushMirror struct {
		RemoteAddress string `xorm:"VARCHAR(2048)"`
	}

	if err := x.Sync(new(Mirror), new(PushMirror)); err != nil {
		return err
	}

	if err := migratePullMirrors(x); err != nil {
		return err
	}

	return migratePushMirrors(x)
}

func migratePullMirrors(x *xorm.Engine) error {
	type Mirror struct {
		ID            int64  `xorm:"pk autoincr"`
		RepoID        int64  `xorm:"INDEX"`
		RemoteAddress string `xorm:"VARCHAR(2048)"`
		RepoOwner     string
		RepoName      string
	}

	sess := x.NewSession()
	defer sess.Close()

	if err := sess.Begin(); err != nil {
		return err
	}

	limit := setting.Database.IterateBufferSize
	if limit <= 0 {
		limit = 50
	}

	start := 0

	for {
		var mirrors []Mirror
		if err := sess.Select("mirror.id, mirror.repo_id, mirror.remote_address, repository.owner_name as repo_owner, repository.name as repo_name").
			Join("INNER", "repository", "repository.id = mirror.repo_id").
			Limit(limit, start).Find(&mirrors); err != nil {
			return err
		}

		if len(mirrors) == 0 {
			break
		}
		start += len(mirrors)

		for _, m := range mirrors {
			remoteAddress, err := repo_model.GetPushMirrorRemoteAddress(m.RepoOwner, m.RepoName, "origin")
			if err != nil {
				return err
			}

			m.RemoteAddress = remoteAddress

			if _, err = sess.ID(m.ID).Cols("remote_address").Update(m); err != nil {
				return err
			}
		}

		if start%1000 == 0 { // avoid a too big transaction
			if err := sess.Commit(); err != nil {
				return err
			}
			if err := sess.Begin(); err != nil {
				return err
			}
		}
	}

	return sess.Commit()
}

func migratePushMirrors(x *xorm.Engine) error {
	type PushMirror struct {
		ID            int64 `xorm:"pk autoincr"`
		RepoID        int64 `xorm:"INDEX"`
		RemoteName    string
		RemoteAddress string `xorm:"VARCHAR(2048)"`
		RepoOwner     string
		RepoName      string
	}

	sess := x.NewSession()
	defer sess.Close()

	if err := sess.Begin(); err != nil {
		return err
	}

	limit := setting.Database.IterateBufferSize
	if limit <= 0 {
		limit = 50
	}

	start := 0

	for {
		var mirrors []PushMirror
		if err := sess.Select("push_mirror.id, push_mirror.repo_id, push_mirror.remote_name, push_mirror.remote_address, repository.owner_name as repo_owner, repository.name as repo_name").
			Join("INNER", "repository", "repository.id = push_mirror.repo_id").
			Limit(limit, start).Find(&mirrors); err != nil {
			return err
		}

		if len(mirrors) == 0 {
			break
		}
		start += len(mirrors)

		for _, m := range mirrors {
			remoteAddress, err := repo_model.GetPushMirrorRemoteAddress(m.RepoOwner, m.RepoName, m.RemoteName)
			if err != nil {
				return err
			}

			m.RemoteAddress = remoteAddress

			if _, err = sess.ID(m.ID).Cols("remote_address").Update(m); err != nil {
				return err
			}
		}

		if start%1000 == 0 { // avoid a too big transaction
			if err := sess.Commit(); err != nil {
				return err
			}
			if err := sess.Begin(); err != nil {
				return err
			}
		}
	}

	return sess.Commit()
}
