package logic

import (
	"context"
	"time"

	"code.justin.tv/live/autohost/internal/hosting/storage"
)

// Helpers has util functions used in endpoint logic methods
type Helpers interface {
	AddedChannel(ctx context.Context, user string, targets []string) error
	DeletedChannel(ctx context.Context, user string, targets []string) error
}

type helpers struct {
	db storage.Storage
}

// NewLogicHelpers instantiates an instance of helpers
func NewLogicHelpers(db storage.Storage) (Helpers, error) {
	return &helpers{
		db: db,
	}, nil
}

func (T *helpers) AddedChannel(ctx context.Context, user string, targets []string) error {
	if len(targets) == 0 {
		return nil
	}

	for _, target := range targets {
		err := T.db.AddToAddedList(ctx, target, user, float64(time.Now().UTC().Unix()))
		if err != nil {
			return err
		}
	}

	return nil
}

func (T *helpers) DeletedChannel(ctx context.Context, user string, targets []string) error {
	if len(targets) == 0 {
		return nil
	}

	for _, target := range targets {
		if err := T.db.DeleteFromAddedList(ctx, target, user); err != nil {
			return err
		}
	}

	return nil
}

// listDiff gets added and removed elements between two lists
// The order of elements in returned lists is guaranteed to be the same as input.
func listDiff(newList, oldList []string) ([]string, []string) {
	newList = dedupe(newList)
	oldList = dedupe(oldList)

	var added []string
	var removed []string

	old := map[string]bool{}
	for _, target := range oldList {
		old[target] = true
	}

	newMap := map[string]bool{}
	for _, target := range newList {
		newMap[target] = true
	}

	// Find entries to be added to oldList
	for _, target := range newList {
		// map value for missing key is false
		if old[target] {
			continue
		}

		added = append(added, target)
	}

	// Find entries to be removed from oldList
	for _, target := range oldList {
		if newMap[target] {
			continue
		}

		removed = append(removed, target)
	}

	return added, removed
}

func dedupe(list []string) []string {
	result := make([]string, 0, len(list))
	set := make(map[string]bool, len(list))
	for _, v := range list {
		if set[v] {
			continue
		}
		set[v] = true

		result = append(result, v)
	}

	return result
}

// removeElementsFromList removes elements from a list
func removeElementsFromList(list, itemsToRemove []string) []string {
	mapOfElementsToRemove := map[string]bool{}
	for _, target := range itemsToRemove {
		mapOfElementsToRemove[target] = true
	}

	var newList []string
	for _, element := range list {
		if _, ok := mapOfElementsToRemove[element]; ok {
			continue
		}

		newList = append(newList, element)
	}

	return newList
}
