package metastore

import (
	"sort"

	"warpbox/lib/config"
)

func ResolveUserPermissions(cfg *config.Config, user User, tags []Tag) EffectivePermissions {
	perms := EffectivePermissions{
		MaxFileSizeBytes:       cfg.DefaultUserMaxFileSizeBytes,
		MaxBoxSizeBytes:        cfg.DefaultUserMaxBoxSizeBytes,
		ZipDownloadAllowed:     cfg.ZipDownloadsEnabled,
		OneTimeDownloadAllowed: cfg.OneTimeDownloadsEnabled,
	}

	expirySet := make(map[int64]bool)
	for _, tag := range tags {
		tagPerms := tag.Permissions
		perms.UploadAllowed = perms.UploadAllowed || tagPerms.UploadAllowed
		perms.OneTimeDownloadAllowed = perms.OneTimeDownloadAllowed || tagPerms.OneTimeDownloadAllowed
		perms.ZipDownloadAllowed = perms.ZipDownloadAllowed || tagPerms.ZipDownloadAllowed
		perms.RenewableAllowed = perms.RenewableAllowed || tagPerms.RenewableAllowed
		perms.AdminAccess = perms.AdminAccess || tagPerms.AdminAccess
		perms.AdminUsersManage = perms.AdminUsersManage || tagPerms.AdminUsersManage
		perms.AdminSettingsManage = perms.AdminSettingsManage || tagPerms.AdminSettingsManage
		perms.AdminBoxesView = perms.AdminBoxesView || tagPerms.AdminBoxesView
		perms.RenewOnAccessSeconds = maxInt64(perms.RenewOnAccessSeconds, tagPerms.RenewOnAccessSeconds)
		perms.RenewOnDownloadSeconds = maxInt64(perms.RenewOnDownloadSeconds, tagPerms.RenewOnDownloadSeconds)
		if tagPerms.MaxFileSizeBytes != nil {
			perms.MaxFileSizeBytes = morePermissiveLimit(perms.MaxFileSizeBytes, *tagPerms.MaxFileSizeBytes)
		}
		if tagPerms.MaxBoxSizeBytes != nil {
			perms.MaxBoxSizeBytes = morePermissiveLimit(perms.MaxBoxSizeBytes, *tagPerms.MaxBoxSizeBytes)
		}
		for _, seconds := range tagPerms.AllowedExpirySeconds {
			if seconds >= 0 {
				expirySet[seconds] = true
			}
		}
	}

	if user.MaxFileSizeBytes != nil {
		perms.MaxFileSizeBytes = morePermissiveLimit(perms.MaxFileSizeBytes, *user.MaxFileSizeBytes)
	}
	if user.MaxBoxSizeBytes != nil {
		perms.MaxBoxSizeBytes = morePermissiveLimit(perms.MaxBoxSizeBytes, *user.MaxBoxSizeBytes)
	}
	if user.MaxExpirySeconds != nil {
		perms.MaxExpirySeconds = *user.MaxExpirySeconds
	}

	perms.MaxFileSizeBytes = capLimit(perms.MaxFileSizeBytes, cfg.GlobalMaxFileSizeBytes)
	perms.MaxBoxSizeBytes = capLimit(perms.MaxBoxSizeBytes, cfg.GlobalMaxBoxSizeBytes)
	perms.AllowedExpirySeconds = sortedExpirySet(expirySet)
	if !cfg.ZipDownloadsEnabled {
		perms.ZipDownloadAllowed = false
	}
	if !cfg.OneTimeDownloadsEnabled {
		perms.OneTimeDownloadAllowed = false
	}
	return perms
}

func ResolveGuestPermissions(cfg *config.Config) EffectivePermissions {
	return EffectivePermissions{
		UploadAllowed:          cfg.GuestUploadsEnabled,
		AllowedExpirySeconds:   guestExpirySeconds(cfg),
		MaxFileSizeBytes:       cfg.GlobalMaxFileSizeBytes,
		MaxBoxSizeBytes:        cfg.GlobalMaxBoxSizeBytes,
		MaxExpirySeconds:       cfg.MaxGuestExpirySeconds,
		OneTimeDownloadAllowed: cfg.OneTimeDownloadsEnabled,
		ZipDownloadAllowed:     cfg.ZipDownloadsEnabled,
		RenewableAllowed:       cfg.RenewOnAccessEnabled || cfg.RenewOnDownloadEnabled,
	}
}

func morePermissiveLimit(current int64, candidate int64) int64 {
	if current == 0 || candidate == 0 {
		return 0
	}
	if candidate > current {
		return candidate
	}
	return current
}

func capLimit(value int64, globalMax int64) int64 {
	if globalMax == 0 {
		return value
	}
	if value == 0 || value > globalMax {
		return globalMax
	}
	return value
}

func sortedExpirySet(expirySet map[int64]bool) []int64 {
	values := make([]int64, 0, len(expirySet))
	for value := range expirySet {
		values = append(values, value)
	}
	sort.Slice(values, func(i int, j int) bool {
		return values[i] < values[j]
	})
	return values
}

func guestExpirySeconds(cfg *config.Config) []int64 {
	values := []int64{}
	if cfg.DefaultGuestExpirySeconds >= 0 {
		values = append(values, cfg.DefaultGuestExpirySeconds)
	}
	if cfg.MaxGuestExpirySeconds > 0 && cfg.MaxGuestExpirySeconds != cfg.DefaultGuestExpirySeconds {
		values = append(values, cfg.MaxGuestExpirySeconds)
	}
	return uniqueInt64s(values)
}

func uniqueInt64s(values []int64) []int64 {
	seen := make(map[int64]bool, len(values))
	out := make([]int64, 0, len(values))
	for _, value := range values {
		if seen[value] {
			continue
		}
		seen[value] = true
		out = append(out, value)
	}
	sort.Slice(out, func(i int, j int) bool {
		return out[i] < out[j]
	})
	return out
}

func maxInt64(a int64, b int64) int64 {
	if b > a {
		return b
	}
	return a
}
