package repository

import (
	"context"
	"errors"

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

type ApprovalRepository interface {
	BaseRepository[entity.Approval]
	FindByTarget(ctx context.Context, workflow string, approvableID uint, modifier func(*gorm.DB) *gorm.DB) ([]entity.Approval, error)
	LatestByTarget(ctx context.Context, workflow string, approvableID uint, modifier func(*gorm.DB) *gorm.DB) (*entity.Approval, error)
	LatestByTargets(ctx context.Context, workflow string, approvableIDs []uint, modifier func(*gorm.DB) *gorm.DB) (map[uint]entity.Approval, error)
	DeleteByTarget(ctx context.Context, workflow string, approvableID uint) error
}

type approvalRepositoryImpl struct {
	*BaseRepositoryImpl[entity.Approval]
}

func NewApprovalRepository(db *gorm.DB) ApprovalRepository {
	return &approvalRepositoryImpl{
		BaseRepositoryImpl: NewBaseRepository[entity.Approval](db),
	}
}

func (r *approvalRepositoryImpl) FindByTarget(
	ctx context.Context,
	workflow string,
	approvableID uint,
	modifier func(*gorm.DB) *gorm.DB,
) ([]entity.Approval, error) {
	var approvals []entity.Approval

	q := r.DB().WithContext(ctx).Where("approvable_type = ? AND approvable_id = ?", workflow, approvableID)
	if modifier != nil {
		q = modifier(q)
	}

	if err := q.Order("action_at ASC").Find(&approvals).Error; err != nil {
		return nil, err
	}
	return approvals, nil
}

func (r *approvalRepositoryImpl) LatestByTarget(
	ctx context.Context,
	workflow string,
	approvableID uint,
	modifier func(*gorm.DB) *gorm.DB,
) (*entity.Approval, error) {
	var approval entity.Approval

	q := r.DB().WithContext(ctx).
		Where("approvable_type = ? AND approvable_id = ?", workflow, approvableID).
		Order("action_at DESC")

	if modifier != nil {
		q = modifier(q)
	}

	if err := q.Limit(1).First(&approval).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}

	return &approval, nil
}

func (r *approvalRepositoryImpl) LatestByTargets(
	ctx context.Context,
	workflow string,
	approvableIDs []uint,
	modifier func(*gorm.DB) *gorm.DB,
) (map[uint]entity.Approval, error) {
	if len(approvableIDs) == 0 {
		return nil, nil
	}

	result := make(map[uint]entity.Approval, len(approvableIDs))

	q := r.DB().WithContext(ctx).
		Select("DISTINCT ON (approvable_id) *").
		Where("approvable_type = ? AND approvable_id IN ?", workflow, approvableIDs).
		Order("approvable_id, action_at DESC")

	if modifier != nil {
		q = modifier(q)
	}

	var approvals []entity.Approval
	if err := q.Find(&approvals).Error; err != nil {
		return nil, err
	}

	for _, approval := range approvals {
		if _, exists := result[approval.ApprovableId]; exists {
			continue
		}
		result[approval.ApprovableId] = approval
	}

	return result, nil
}

func (r *approvalRepositoryImpl) DeleteByTarget(
	ctx context.Context,
	workflow string,
	approvableID uint,
) error {
	return r.DB().WithContext(ctx).
		Where("approvable_type = ? AND approvable_id = ?", workflow, approvableID).
		Delete(&entity.Approval{}).Error
}
