package nextcloud

import (
	"net/url"
	"strings"
)

// WebDAVPath builds an encoded WebDAV URL for a logical client path (may contain spaces).
func (c *Client) WebDAVPath(userID, path string) string {
	userSeg := url.PathEscape(strings.TrimSpace(userID))
	logical := strings.Trim(path, "/")
	var encoded string
	if logical == "" {
		encoded = ""
	} else {
		parts := strings.Split(logical, "/")
		for i, p := range parts {
			parts[i] = url.PathEscape(p)
		}
		encoded = strings.Join(parts, "/")
	}
	if encoded == "" {
		return "/remote.php/dav/files/" + userSeg
	}
	return "/remote.php/dav/files/" + userSeg + "/" + encoded
}

// SameServerDestinationHeader builds a relative WebDAV Destination header value.
// Nextcloud/Sabre rejects absolute URIs when the host differs from OVERWRITEHOST
// (e.g. internal http://nextcloud:80 vs public http://localhost/cloud).
func SameServerDestinationHeader(davPath string) string {
	if !strings.HasPrefix(davPath, "/") {
		return "/" + davPath
	}
	return davPath
}

func decodeDAVSegment(seg string) string {
	if seg == "" {
		return seg
	}
	dec, err := url.PathUnescape(seg)
	if err != nil {
		return seg
	}
	return dec
}

// decodeDAVPath turns a slash-separated (possibly encoded) path into a logical path.
func decodeDAVPath(p string) string {
	p = strings.TrimSpace(p)
	p = strings.Trim(p, "/")
	if p == "" {
		return "/"
	}
	parts := strings.Split(p, "/")
	for i, seg := range parts {
		parts[i] = decodeDAVSegment(seg)
	}
	return "/" + strings.Join(parts, "/")
}

// clientPathFromDAVHref maps a WebDAV href to a logical path (/Folder/My File.docx).
func clientPathFromDAVHref(href string) string {
	href = strings.TrimSpace(href)
	if href == "" {
		return "/"
	}
	if marker := "/dav/files/"; strings.Contains(href, marker) {
		idx := strings.Index(href, marker)
		rest := strings.TrimSuffix(href[idx+len(marker):], "/")
		slash := strings.Index(rest, "/")
		if slash < 0 {
			return "/"
		}
		return decodeDAVPath("/" + rest[slash+1:])
	}
	if i := strings.LastIndex(href, "/trash/"); i >= 0 {
		return decodeDAVPath("/" + href[i+len("/trash/"):])
	}
	return decodeDAVPath(href)
}

func fileNameFromDAVProp(displayName, href string) string {
	if dn := strings.TrimSpace(displayName); dn != "" {
		return dn
	}
	href = strings.TrimSuffix(strings.TrimSpace(href), "/")
	if href == "" {
		return ""
	}
	if i := strings.LastIndex(href, "/"); i >= 0 {
		href = href[i+1:]
	}
	return decodeDAVSegment(href)
}

// NormalizeClientPath decodes segments and ensures a leading slash.
func NormalizeClientPath(path string) string {
	return decodeDAVPath(path)
}

// JoinClientPath joins a directory path with a file or folder name.
func JoinClientPath(dir, name string) string {
	name = strings.TrimSpace(name)
	dir = NormalizeClientPath(dir)
	if name == "" {
		return dir
	}
	if dir == "/" {
		return "/" + name
	}
	return dir + "/" + name
}

// NormalizeClientFilePath maps OCS/WebDAV paths to logical paths under the user files root.
func NormalizeClientFilePath(userID, path string) string {
	path = strings.TrimSpace(path)
	if path == "" {
		return "/"
	}
	path = NormalizeClientPath(path)
	uid := strings.TrimSpace(userID)
	if uid == "" {
		return path
	}
	trimmed := strings.Trim(path, "/")
	if trimmed == "" {
		return "/"
	}
	parts := strings.Split(trimmed, "/")
	if len(parts) >= 2 {
		head := decodeDAVSegment(parts[0])
		if head == uid && parts[1] == "files" {
			return NormalizeClientPath("/" + strings.Join(parts[2:], "/"))
		}
		if parts[0] == "files" && decodeDAVSegment(parts[1]) == uid {
			return NormalizeClientPath("/" + strings.Join(parts[2:], "/"))
		}
	}
	return path
}

// FileNameFromClientPath returns the storage basename for a logical client path.
func FileNameFromClientPath(path string) string {
	return pathBaseName(NormalizeClientPath(path))
}

// SyncFileDisplayName aligns display name with the storage path basename when present.
func SyncFileDisplayName(path, name string) string {
	if bn := FileNameFromClientPath(path); bn != "" {
		return bn
	}
	return strings.TrimSpace(name)
}

// EnsureClientFilePath joins name when path is a parent directory (Nextcloud recent API).
func EnsureClientFilePath(path, name string) string {
	path = NormalizeClientPath(path)
	name = strings.TrimSpace(name)
	if name == "" {
		return path
	}
	if path == "/" {
		return "/" + name
	}
	if strings.HasSuffix(path, "/"+name) {
		return path
	}
	base := path[strings.LastIndex(path, "/")+1:]
	if base == name {
		return path
	}
	return JoinClientPath(path, name)
}

// ResolvePropfindClientPath resolves a PROPFIND child href against the listed directory.
func ResolvePropfindClientPath(listDir, href, fileName string) string {
	if strings.Contains(href, "/dav/files/") {
		return clientPathFromDAVHref(href)
	}
	base := NormalizeClientPath(listDir)
	rel := strings.TrimPrefix(clientPathFromDAVHref(href), "/")
	if rel == "" {
		rel = strings.TrimSpace(fileName)
	}
	if rel == "" {
		return base
	}
	if base == "/" {
		return "/" + rel
	}
	return base + "/" + rel
}
