package tunnel

import (
	"fmt"
	"path/filepath"

	cfdflags "github.com/cloudflare/cloudflared/cmd/cloudflared/flags"
	"github.com/cloudflare/cloudflared/config"
	"github.com/cloudflare/cloudflared/credentials"

	"github.com/google/uuid"
	"github.com/rs/zerolog"
	"github.com/urfave/cli/v2"
)

// CredFinder can find the tunnel credentials file.
type CredFinder interface {
	Path() (string, error)
}

// Implements CredFinder and looks for the credentials file at the given
// filepath.
type staticPath struct {
	filePath string
	fs       fileSystem
}

func newStaticPath(filePath string, fs fileSystem) CredFinder {
	return staticPath{
		filePath: filePath,
		fs:       fs,
	}
}

func (a staticPath) Path() (string, error) {
	if a.filePath != "" && a.fs.validFilePath(a.filePath) {
		return a.filePath, nil
	}
	return "", fmt.Errorf("Tunnel credentials file '%s' doesn't exist or is not a file", a.filePath)
}

// Implements CredFinder and looks for the credentials file in several directories
// searching for a file named <id>.json
type searchByID struct {
	id  uuid.UUID
	c   *cli.Context
	log *zerolog.Logger
	fs  fileSystem
}

func newSearchByID(id uuid.UUID, c *cli.Context, log *zerolog.Logger, fs fileSystem) CredFinder {
	return searchByID{
		id:  id,
		c:   c,
		log: log,
		fs:  fs,
	}
}

func (s searchByID) Path() (string, error) {
	originCertPath := s.c.String(cfdflags.OriginCert)
	originCertLog := s.log.With().
		Str("originCertPath", originCertPath).
		Logger()

	// Fallback to look for tunnel credentials in the origin cert directory
	if originCertPath, err := credentials.FindOriginCert(originCertPath, &originCertLog); err == nil {
		originCertDir := filepath.Dir(originCertPath)
		if filePath, err := tunnelFilePath(s.id, originCertDir); err == nil {
			if s.fs.validFilePath(filePath) {
				return filePath, nil
			}
		}
	}

	// Last resort look under default config directories
	for _, configDir := range config.DefaultConfigSearchDirectories() {
		if filePath, err := tunnelFilePath(s.id, configDir); err == nil {
			if s.fs.validFilePath(filePath) {
				return filePath, nil
			}
		}
	}
	return "", fmt.Errorf("tunnel credentials file not found")
}
