package path

import (
	"errors"
	"path"
	"strings"

	u "github.com/ipfs/go-ipfs/util"

	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
)

// ErrBadPath is returned when a given path is incorrectly formatted
var ErrBadPath = errors.New("invalid ipfs ref path")

// TODO: debate making this a private struct wrapped in a public interface
// would allow us to control creation, and cache segments.
type Path string

// FromString safely converts a string type to a Path type
func FromString(s string) Path {
	return Path(s)
}

// FromKey safely converts a Key type to a Path type
func FromKey(k u.Key) Path {
	return Path("/ipfs/" + k.String())
}

func (p Path) Segments() []string {
	cleaned := path.Clean(string(p))
	segments := strings.Split(cleaned, "/")

	// Ignore leading slash
	if len(segments[0]) == 0 {
		segments = segments[1:]
	}

	return segments
}

func (p Path) String() string {
	return string(p)
}

func FromSegments(seg ...string) (Path, error) {
	var pref string
	if seg[0] == "ipfs" || seg[0] == "ipns" {
		pref = "/"
	}
	return ParsePath(pref + strings.Join(seg, "/"))
}

func ParsePath(txt string) (Path, error) {
	parts := strings.Split(txt, "/")
	if len(parts) == 1 {
		kp, err := ParseKeyToPath(txt)
		if err == nil {
			return kp, nil
		}
	}
	if len(parts) < 3 {
		return "", ErrBadPath
	}

	if parts[0] != "" {
		return "", ErrBadPath
	}

	if parts[1] != "ipfs" && parts[1] != "ipns" {
		return "", ErrBadPath
	}

	_, err := ParseKeyToPath(parts[2])
	if err != nil {
		return "", err
	}

	return Path(txt), nil
}

func ParseKeyToPath(txt string) (Path, error) {
	chk := b58.Decode(txt)
	if len(chk) == 0 {
		return "", errors.New("not a key")
	}

	_, err := mh.Cast(chk)
	if err != nil {
		return "", err
	}
	return FromKey(u.Key(chk)), nil
}

func (p *Path) IsValid() error {
	_, err := ParsePath(p.String())
	return err
}
