package repository

import (
	"context"
	"errors"

	"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	"gorm.io/gorm"
)

type FcrRepository interface {
	repository.BaseRepository[entity.Fcr]
	NameExists(ctx context.Context, name string, excludeID *uint) (bool, error)
	SyncStandardsDiff(ctx context.Context, tx *gorm.DB, fcrID uint, standards []entity.FcrStandard) error
}

type FcrRepositoryImpl struct {
	*repository.BaseRepositoryImpl[entity.Fcr]
}

func NewFcrRepository(db *gorm.DB) FcrRepository {
	return &FcrRepositoryImpl{
		BaseRepositoryImpl: repository.NewBaseRepository[entity.Fcr](db),
	}
}

func (r *FcrRepositoryImpl) NameExists(ctx context.Context, name string, excludeID *uint) (bool, error) {
	return repository.ExistsByName[entity.Fcr](ctx, r.DB(), name, excludeID)
}

func (r *FcrRepositoryImpl) SyncStandardsDiff(ctx context.Context, tx *gorm.DB, fcrID uint, standards []entity.FcrStandard) error {
	db := tx
	if db == nil {
		db = r.DB()
	}

	var existing []entity.FcrStandard
	if err := db.WithContext(ctx).
		Where("fcr_id = ?", fcrID).
		Find(&existing).
		Error; err != nil {
		return err
	}

	existingMap := make(map[float64]entity.FcrStandard)
	for _, st := range existing {
		existingMap[st.Weight] = st
	}

	newMap := make(map[float64]entity.FcrStandard)
	for _, st := range standards {
		st.FcrID = fcrID
		newMap[st.Weight] = st
	}

	baseRepo := repository.NewBaseRepository[entity.FcrStandard](db)

	for weight, newStd := range newMap {
		if current, ok := existingMap[weight]; ok {
			if current.FcrNumber != newStd.FcrNumber || current.Mortality != newStd.Mortality {
				update := map[string]any{
					"fcr_number": newStd.FcrNumber,
					"mortality":  newStd.Mortality,
				}
				if err := baseRepo.PatchOne(ctx, current.Id, update, nil); err != nil {
					return err
				}
			}
		} else {
			entry := newStd
			if err := baseRepo.CreateOne(ctx, &entry, nil); err != nil {
				return err
			}
		}
	}

	for weight, current := range existingMap {
		if _, keep := newMap[weight]; !keep {
			if err := baseRepo.DeleteOne(ctx, current.Id); err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					continue
				}
				return err
			}
		}
	}

	return nil
}
