package storage

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path"
	"strings"

	"go.b0esche.cloud/backend/internal/config"
)

type WebDAVClient struct {
	baseURL    string
	user       string
	pass       string
	basePrefix string
	httpClient *http.Client
}

// NewWebDAVClient returns nil if no Nextcloud URL configured
func NewWebDAVClient(cfg *config.Config) *WebDAVClient {
	if cfg == nil || strings.TrimSpace(cfg.NextcloudURL) == "" {
		fmt.Printf("[WEBDAV] No Nextcloud URL configured, WebDAV client is nil\n")
		return nil
	}
	u := strings.TrimRight(cfg.NextcloudURL, "/")
	base := cfg.NextcloudBase
	if base == "" {
		base = "/"
	}
	fmt.Printf("[WEBDAV] Initializing WebDAV client - URL: %s, User: %s, BasePath: %s\n", u, cfg.NextcloudUser, base)
	return &WebDAVClient{
		baseURL:    u,
		user:       cfg.NextcloudUser,
		pass:       cfg.NextcloudPass,
		basePrefix: strings.TrimRight(base, "/"),
		httpClient: &http.Client{},
	}
}

// ensureParent creates intermediate collections using MKCOL. Ignoring errors when already exists.
func (c *WebDAVClient) ensureParent(ctx context.Context, remotePath string) error {
	// build incremental paths
	dir := path.Dir(remotePath)
	if dir == "." || dir == "/" || dir == "" {
		return nil
	}
	// split and build prefixes
	parts := strings.Split(strings.Trim(dir, "/"), "/")
	cur := c.basePrefix
	for _, p := range parts {
		cur = path.Join(cur, p)
		mkurl := fmt.Sprintf("%s%s", c.baseURL, cur)
		req, _ := http.NewRequestWithContext(ctx, "MKCOL", mkurl, nil)
		if c.user != "" {
			req.SetBasicAuth(c.user, c.pass)
		}
		resp, err := c.httpClient.Do(req)
		if err != nil {
			return err
		}
		resp.Body.Close()
		// 201 created, 405 exists — ignore
		if resp.StatusCode == 201 || resp.StatusCode == 405 {
			continue
		}
	}
	return nil
}

// Upload streams the content to the remotePath using HTTP PUT (WebDAV). remotePath should be absolute under basePrefix.
func (c *WebDAVClient) Upload(ctx context.Context, remotePath string, r io.Reader, size int64) error {
	if c == nil {
		return fmt.Errorf("no webdav client configured")
	}
	// Ensure parent collections
	if err := c.ensureParent(ctx, remotePath); err != nil {
		return err
	}
	// Construct URL
	// remotePath might be like /orgs/<id>/file.txt; ensure it joins to basePrefix
	rel := strings.TrimLeft(remotePath, "/")
	u := c.basePrefix
	if u == "/" || u == "" {
		u = ""
	}
	u = strings.TrimRight(u, "/")

	var full string
	if u == "" {
		full = fmt.Sprintf("%s/%s", c.baseURL, url.PathEscape(rel))
	} else {
		full = fmt.Sprintf("%s%s/%s", c.baseURL, u, url.PathEscape(rel))
	}
	full = strings.ReplaceAll(full, "%2F", "/")

	fmt.Printf("[WEBDAV-UPLOAD] BaseURL: %s, BasePrefix: %s, RemotePath: %s, Full URL: %s\n", c.baseURL, c.basePrefix, remotePath, full)

	req, err := http.NewRequestWithContext(ctx, "PUT", full, r)
	if err != nil {
		return err
	}
	if size > 0 {
		req.ContentLength = size
	}
	if c.user != "" {
		req.SetBasicAuth(c.user, c.pass)
	}
	req.Header.Set("Content-Type", "application/octet-stream")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}
	body, _ := io.ReadAll(resp.Body)
	return fmt.Errorf("webdav upload failed: %d %s", resp.StatusCode, string(body))
}

// Download retrieves a file from the remotePath using HTTP GET (WebDAV).
func (c *WebDAVClient) Download(ctx context.Context, remotePath string, rangeHeader string) (*http.Response, error) {
	if c == nil {
		return nil, fmt.Errorf("no webdav client configured")
	}

	rel := strings.TrimLeft(remotePath, "/")
	u := c.basePrefix
	if u == "/" || u == "" {
		u = ""
	}
	u = strings.TrimRight(u, "/")

	var full string
	if u == "" {
		full = fmt.Sprintf("%s/%s", c.baseURL, url.PathEscape(rel))
	} else {
		full = fmt.Sprintf("%s%s/%s", c.baseURL, u, url.PathEscape(rel))
	}
	full = strings.ReplaceAll(full, "%2F", "/")

	req, err := http.NewRequestWithContext(ctx, "GET", full, nil)
	if err != nil {
		return nil, err
	}
	if c.user != "" {
		req.SetBasicAuth(c.user, c.pass)
	}
	if rangeHeader != "" {
		req.Header.Set("Range", rangeHeader)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return resp, nil
	}

	body, _ := io.ReadAll(resp.Body)
	resp.Body.Close()
	return nil, fmt.Errorf("webdav download failed: %d %s", resp.StatusCode, string(body))
}

// Delete removes a file or collection from the remotePath using HTTP DELETE (WebDAV).
func (c *WebDAVClient) Delete(ctx context.Context, remotePath string) error {
	if c == nil {
		return fmt.Errorf("no webdav client configured")
	}

	rel := strings.TrimLeft(remotePath, "/")
	u := c.basePrefix
	if u == "/" || u == "" {
		u = ""
	}
	u = strings.TrimRight(u, "/")

	var full string
	if u == "" {
		full = fmt.Sprintf("%s/%s", c.baseURL, url.PathEscape(rel))
	} else {
		full = fmt.Sprintf("%s%s/%s", c.baseURL, u, url.PathEscape(rel))
	}
	full = strings.ReplaceAll(full, "%2F", "/")

	req, err := http.NewRequestWithContext(ctx, "DELETE", full, nil)
	if err != nil {
		return err
	}
	if c.user != "" {
		req.SetBasicAuth(c.user, c.pass)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}

	// 404 means already deleted, consider it success
	if resp.StatusCode == 404 {
		return nil
	}

	body, _ := io.ReadAll(resp.Body)
	return fmt.Errorf("webdav delete failed: %d %s", resp.StatusCode, string(body))
}

// Move moves/renames a file using WebDAV MOVE method
func (c *WebDAVClient) Move(ctx context.Context, sourcePath, targetPath string) error {
	if c == nil {
		return fmt.Errorf("no webdav client configured")
	}

	sourceRel := strings.TrimLeft(sourcePath, "/")
	targetRel := strings.TrimLeft(targetPath, "/")

	u := c.basePrefix
	if u == "/" || u == "" {
		u = ""
	}
	u = strings.TrimRight(u, "/")

	// Build source URL
	var sourceURL string
	if u == "" {
		sourceURL = fmt.Sprintf("%s/%s", c.baseURL, url.PathEscape(sourceRel))
	} else {
		sourceURL = fmt.Sprintf("%s%s/%s", c.baseURL, u, url.PathEscape(sourceRel))
	}
	sourceURL = strings.ReplaceAll(sourceURL, "%2F", "/")

	// Build target URL
	var targetURL string
	if u == "" {
		targetURL = fmt.Sprintf("%s/%s", c.baseURL, url.PathEscape(targetRel))
	} else {
		targetURL = fmt.Sprintf("%s%s/%s", c.baseURL, u, url.PathEscape(targetRel))
	}
	targetURL = strings.ReplaceAll(targetURL, "%2F", "/")

	req, err := http.NewRequestWithContext(ctx, "MOVE", sourceURL, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Destination", targetURL)
	if c.user != "" {
		req.SetBasicAuth(c.user, c.pass)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}

	body, _ := io.ReadAll(resp.Body)
	return fmt.Errorf("webdav move failed: %d %s", resp.StatusCode, string(body))
}
