// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.

package target

import (
	"errors"
	"fmt"
	"net/url"
	"strings"
	"path/filepath"
	"log/slog"
)

var (
	ErrUnknownTargetType        = errors.New("Unknown target type")
	TargetTypes          *Types = NewTypes()
)

type TypeName string //`json:"type"`

type TypeFactory func(*url.URL) DocTarget

type Types struct {
	registry map[string]TypeFactory
}

func NewTypes() *Types {
	return &Types{registry: make(map[string]TypeFactory)}
}

func (t *Types) Register(names []string, factory TypeFactory) {
	for _,name := range names {
		t.registry[name] = factory
	}
}

func (t *Types) FromExtension(path string) (TypeFactory, error) {
	elements := strings.Split(path, ".")
	numberOfElements := len(elements)
	if numberOfElements > 2 {
		if src := t.Get(strings.Join(elements[numberOfElements - 2: numberOfElements - 1], ".")); src != nil {
			return src, nil
		}
	}
	if src := t.Get(elements[numberOfElements - 1]); src != nil {
		return src, nil
	}
	return nil, fmt.Errorf("%w: %s", ErrUnknownTargetType, path)
}

func (t *Types) New(uri string) (DocTarget, error) {
	if uri == "" {
		uri = "file://-"
	}

	u, e := url.Parse(uri)
	if u == nil || e != nil {
		return nil, fmt.Errorf("%w: %s", ErrUnknownTargetType, e)
	}

	if u.Scheme == "" {
		u.Scheme = "file"
	}

	path := filepath.Join(u.Hostname(), u.Path)
	if d, lookupErr := t.FromExtension(path); d != nil {
		slog.Info("Target.New", "target", t, "err", lookupErr)
		return d(u), lookupErr
	} else {
		slog.Info("Target.New", "target", t, "err", lookupErr)
	}

	if r, ok := t.registry[u.Scheme]; ok {
		return r(u), nil
	}
	return nil, fmt.Errorf("%w: %s", ErrUnknownTargetType, u.Scheme)
}

func (t *Types) Has(typename string) bool {
	if _, ok := t.registry[typename]; ok {
		return true
	}
	return false
}

func (t *Types) Get(typename string) TypeFactory {
	if d, ok := t.registry[typename]; ok {
		return d
	}
	return nil
}

func (n *TypeName) UnmarshalJSON(b []byte) error {
	TargetTypeName := strings.Trim(string(b), "\"")
	if TargetTypes.Has(TargetTypeName) {
		*n = TypeName(TargetTypeName)
		return nil
	}
	return fmt.Errorf("%w: %s", ErrUnknownTargetType, TargetTypeName)
}
