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

package project

import (
	"context"
	"strconv"

	"code.gitea.io/gitea/models/db"

	"xorm.io/builder"
)

// CountProjectColumns returns the total number of columns for a project
func CountProjectColumns(ctx context.Context, projectID int64) (int64, error) {
	return db.GetEngine(ctx).Where("project_id=?", projectID).Count(&Column{})
}

// GetProjectColumns returns a list of columns for a project with pagination
func GetProjectColumns(ctx context.Context, projectID int64, opts db.ListOptions) (ColumnList, error) {
	columns := make([]*Column, 0, opts.PageSize)
	s := db.GetEngine(ctx).Where("project_id=?", projectID).OrderBy("sorting, id")
	if !opts.IsListAll() {
		db.SetSessionPagination(s, &opts)
	}
	if err := s.Find(&columns); err != nil {
		return nil, err
	}
	return columns, nil
}

// LoadIssueCounts populates NumIssues, NumOpenIssues, and NumClosedIssues on
// every column in the list using two grouped queries against project_issue
// joined with issue. Columns with no attached issues stay at zero counts —
// nothing else has to be wired up by the caller.
func (cl ColumnList) LoadIssueCounts(ctx context.Context) error {
	if len(cl) == 0 {
		return nil
	}

	columnIDs := make([]int64, 0, len(cl))
	for _, c := range cl {
		columnIDs = append(columnIDs, c.ID)
	}

	openCounts, err := countColumnIssuesByState(ctx, columnIDs, false)
	if err != nil {
		return err
	}
	closedCounts, err := countColumnIssuesByState(ctx, columnIDs, true)
	if err != nil {
		return err
	}

	for _, c := range cl {
		c.NumOpenIssues = openCounts[c.ID]
		c.NumClosedIssues = closedCounts[c.ID]
		c.NumIssues = c.NumOpenIssues + c.NumClosedIssues
	}
	return nil
}

func countColumnIssuesByState(ctx context.Context, columnIDs []int64, isClosed bool) (map[int64]int64, error) {
	out := make(map[int64]int64, len(columnIDs))
	cond := builder.In("project_issue.project_board_id", columnIDs).
		And(builder.Eq{"issue.is_closed": isClosed})
	sub := builder.Select("project_issue.project_board_id AS project_board_id", "COUNT(*) AS cnt").
		From("project_issue").
		InnerJoin("issue", "issue.id = project_issue.issue_id").
		Where(cond).
		GroupBy("project_issue.project_board_id")
	rows, err := db.GetEngine(ctx).Query(sub)
	if err != nil {
		return nil, err
	}
	for _, r := range rows {
		columnID, _ := strconv.ParseInt(string(r["project_board_id"]), 10, 64)
		cnt, _ := strconv.ParseInt(string(r["cnt"]), 10, 64)
		out[columnID] = cnt
	}
	return out, nil
}

func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) {
	columns := make([]*Column, 0, len(columnsIDs))
	if len(columnsIDs) == 0 {
		return columns, nil
	}
	if err := db.GetEngine(ctx).
		Where("project_id =?", projectID).
		In("id", columnsIDs).
		OrderBy("sorting").Find(&columns); err != nil {
		return nil, err
	}
	return columns, nil
}
