// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later

package updateserver

import (
	"context"

	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil"
)

func init() {
	db.RegisterModel(new(ReleaseStreamMap))
}

// ReleaseStreamMap manually assigns a release to an update stream.
// When present, overrides automatic stream detection from tag names.
type ReleaseStreamMap struct {
	ID          int64              `xorm:"pk autoincr"`
	ReleaseID   int64              `xorm:"UNIQUE NOT NULL INDEX"` // FK to release
	RepoID      int64              `xorm:"NOT NULL INDEX"`        // for fast repo-scoped queries
	StreamName  string             `xorm:"NOT NULL"`              // e.g. "stable", "release-candidate"
	CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
}

func (ReleaseStreamMap) TableName() string {
	return "release_stream_map"
}

// SetReleaseStream assigns or updates the stream for a release.
func SetReleaseStream(ctx context.Context, releaseID, repoID int64, streamName string) error {
	existing := new(ReleaseStreamMap)
	has, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Get(existing)
	if err != nil {
		return err
	}

	if has {
		existing.StreamName = streamName
		_, err = db.GetEngine(ctx).ID(existing.ID).Cols("stream_name").Update(existing)
		return err
	}

	_, err = db.GetEngine(ctx).Insert(&ReleaseStreamMap{
		ReleaseID:  releaseID,
		RepoID:     repoID,
		StreamName: streamName,
	})
	return err
}

// GetReleaseStream returns the manually assigned stream for a release, or empty string.
func GetReleaseStream(ctx context.Context, releaseID int64) string {
	m := new(ReleaseStreamMap)
	has, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Get(m)
	if err != nil || !has {
		return ""
	}
	return m.StreamName
}

// GetStreamMapForRepo returns all manual stream assignments for a repo.
func GetStreamMapForRepo(ctx context.Context, repoID int64) (map[int64]string, error) {
	var maps []ReleaseStreamMap
	if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&maps); err != nil {
		return nil, err
	}
	result := make(map[int64]string, len(maps))
	for _, m := range maps {
		result[m.ReleaseID] = m.StreamName
	}
	return result, nil
}

// ResolveReleaseStream returns the stream for a release: manual mapping first, auto-detect fallback.
func ResolveReleaseStream(ctx context.Context, releaseID int64, tagName string, isPrerelease bool, streams []StreamDef) string {
	if manual := GetReleaseStream(ctx, releaseID); manual != "" {
		return manual
	}
	return MatchStreamFromTag(tagName, isPrerelease, streams)
}

// DeleteReleaseStream removes the manual stream assignment for a release.
func DeleteReleaseStream(ctx context.Context, releaseID int64) error {
	_, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Delete(new(ReleaseStreamMap))
	return err
}
