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

package v1_22

import (
	"forgejo.org/modules/setting"
	"forgejo.org/modules/timeutil"

	"xorm.io/xorm"
)

// CheckProjectColumnsConsistency ensures there is exactly one default board per project present
func CheckProjectColumnsConsistency(x *xorm.Engine) error {
	sess := x.NewSession()
	defer sess.Close()

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

	type Project struct {
		ID        int64
		CreatorID int64
		BoardID   int64
	}

	type ProjectBoard struct {
		ID      int64 `xorm:"pk autoincr"`
		Title   string
		Default bool   `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
		Sorting int8   `xorm:"NOT NULL DEFAULT 0"`
		Color   string `xorm:"VARCHAR(7)"`

		ProjectID int64 `xorm:"INDEX NOT NULL"`
		CreatorID int64 `xorm:"NOT NULL"`

		CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
		UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
	}

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

		// all these projects without defaults will be fixed in the same loop, so
		// we just need to always get projects without defaults until no such project
		var projects []*Project
		if err := sess.Select("project.id as id, project.creator_id, project_board.id as board_id").
			Join("LEFT", "project_board", "project_board.project_id = project.id AND project_board.`default`=?", true).
			Where("project_board.id is NULL OR project_board.id = 0").
			Limit(limit).
			Find(&projects); err != nil {
			return err
		}

		for _, p := range projects {
			if _, err := sess.Insert(ProjectBoard{
				ProjectID: p.ID,
				Default:   true,
				Title:     "Uncategorized",
				CreatorID: p.CreatorID,
			}); err != nil {
				return err
			}
		}
		if err := sess.Commit(); err != nil {
			return err
		}

		if len(projects) == 0 {
			break
		}
	}
	sess.Close()

	return removeDuplicatedBoardDefault(x)
}

func removeDuplicatedBoardDefault(x *xorm.Engine) error {
	type ProjectInfo struct {
		ProjectID  int64
		DefaultNum int
	}
	var projects []ProjectInfo
	if err := x.Select("project_id, count(*) AS default_num").
		Table("project_board").
		Where("`default` = ?", true).
		GroupBy("project_id").
		Having("count(*) > 1").
		Find(&projects); err != nil {
		return err
	}

	for _, project := range projects {
		if _, err := x.Where("project_id=?", project.ProjectID).
			Table("project_board").
			Limit(project.DefaultNum - 1).
			Update(map[string]bool{
				"`default`": false,
			}); err != nil {
			return err
		}
	}
	return nil
}
