/**
# Copyright (c) 2024, HCUOpt CORPORATION.  All rights reserved.
**/

package discover

import (
	"dcu-container-toolkit/internal/logger"
	"dcu-container-toolkit/internal/lookup"
	"fmt"
	"path/filepath"
	"strings"
	"sync"
)

// mounts is a generic discoverer for Mounts. It is customized by specifying the
// required entities as a list and a Locator that is used to find the target mounts
// based on the entry in the list.
type mounts struct {
	None
	logger   logger.Interface
	lookup   lookup.Locator
	root     string
	required []string
	sync.Mutex
	cache []Mount
}

var _ Discover = (*mounts)(nil)

// NewMounts creates a discoverer for the required mounts using the specified locator.
func NewMounts(logger logger.Interface, lookup lookup.Locator, root string, required []string) Discover {
	return newMounts(logger, lookup, root, required)
}

// newMounts creates a discoverer for the required mounts using the specified locator.
func newMounts(logger logger.Interface, lookup lookup.Locator, root string, required []string) *mounts {
	return &mounts{
		logger:   logger,
		lookup:   lookup,
		root:     filepath.Join("/", root),
		required: required,
	}
}

func (d *mounts) Mounts() ([]Mount, error) {
	if d.lookup == nil {
		return nil, fmt.Errorf("no lookup defined")
	}

	if d.cache != nil {
		d.logger.Debugf("returning cached mounts")
		return d.cache, nil
	}

	d.Lock()
	defer d.Unlock()

	d.logger.Infof("Locating %v in %v", d.required, d.root)

	uniqueMounts := make(map[string]Mount)

	for _, candidate := range d.required {
		d.logger.Infof("Locating %v", candidate)
		located, err := d.lookup.Locate(candidate)
		if err != nil {
			d.logger.Warningf("Could not locate %v: %v", candidate, err)
			continue
		}
		if len(located) == 0 {
			d.logger.Warningf("Missing %v", candidate)
			continue
		}
		d.logger.Debugf("Located %v as %v", candidate, located)
		for _, p := range located {
			if _, ok := uniqueMounts[p]; ok {
				d.logger.Debugf("Skipping duplicate mount %v", p)
				continue
			}

			r := d.relativeTo(p)
			if r == "" {
				r = p
			}

			d.logger.Infof("Selecting %v as %v", p, r)
			uniqueMounts[p] = Mount{
				HostPath: p,
				Path:     r,
				Options: []string{
					"ro",
					"nosuid",
					"nodev",
					"bind",
				},
			}
		}
	}

	var mounts []Mount
	for _, m := range uniqueMounts {
		mounts = append(mounts, m)
	}
	d.cache = mounts

	return d.cache, nil
}

// relativeTo returns the path relative to the root for the file locator
func (d *mounts) relativeTo(path string) string {
	if d.root == "/" {
		return path
	}

	return strings.TrimPrefix(path, d.root)
}
