package nextcloud

import (
	"context"
	"io"
	"net/http"
	"strings"
)

func (c *Client) ListFilesAtDAV(ctx context.Context, userID, davPath, logicalPath string) ([]FileInfo, error) {
	body := propfindBody
	resp, err := c.DoAsUser(ctx, "PROPFIND", davPath, strings.NewReader(body), userID, map[string]string{
		"Depth":        "1",
		"Content-Type": "application/xml",
	})
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 207 {
		return nil, &HTTPStatusError{Operation: "propfind", StatusCode: resp.StatusCode}
	}
	return parsePropfindResponse(resp.Body, logicalPath)
}

func (c *Client) StatFileAtDAV(ctx context.Context, userID, davPath, logicalPath string) (FileInfo, error) {
	body := propfindBody
	resp, err := c.DoAsUser(ctx, "PROPFIND", davPath, strings.NewReader(body), userID, map[string]string{
		"Depth":        "0",
		"Content-Type": "application/xml",
	})
	if err != nil {
		return FileInfo{}, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 207 {
		return FileInfo{}, &HTTPStatusError{Operation: "propfind", StatusCode: resp.StatusCode}
	}
	files, err := parsePropfindResponse(resp.Body, logicalPath)
	if err != nil {
		return FileInfo{}, err
	}
	if len(files) == 0 {
		return FileInfo{}, &HTTPStatusError{Operation: "stat", StatusCode: http.StatusNotFound}
	}
	return files[0], nil
}

func (c *Client) UploadAtDAV(ctx context.Context, userID, davPath, contentType string, content io.Reader) error {
	if contentType == "" {
		contentType = "application/octet-stream"
	}
	resp, err := c.DoAsUser(ctx, "PUT", davPath, content, userID, map[string]string{
		"Content-Type": contentType,
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
		return &HTTPStatusError{Operation: "upload", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) CreateFolderAtDAV(ctx context.Context, userID, davPath string) error {
	resp, err := c.DoAsUser(ctx, "MKCOL", davPath, nil, userID, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated {
		return &HTTPStatusError{Operation: "mkcol", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) DeleteAtDAV(ctx context.Context, userID, davPath string) error {
	resp, err := c.DoAsUser(ctx, "DELETE", davPath, nil, userID, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "delete", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) MoveAtDAV(ctx context.Context, userID, srcDAV, destDAV string) error {
	resp, err := c.DoAsUser(ctx, "MOVE", srcDAV, nil, userID, map[string]string{
		"Destination": c.webDAVDestination(destDAV),
		"Overwrite":   "T",
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
		return &HTTPStatusError{Operation: "move", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) CopyAtDAV(ctx context.Context, userID, srcDAV, destDAV string) error {
	resp, err := c.DoAsUser(ctx, "COPY", srcDAV, nil, userID, map[string]string{
		"Destination": c.webDAVDestination(destDAV),
		"Overwrite":   "T",
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
		return &HTTPStatusError{Operation: "copy", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) DownloadAtDAV(ctx context.Context, userID, davPath string) (io.ReadCloser, string, error) {
	resp, err := c.DoAsUser(ctx, "GET", davPath, nil, userID, nil)
	if err != nil {
		return nil, "", err
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, "", &HTTPStatusError{Operation: "download", StatusCode: resp.StatusCode}
	}
	contentType := resp.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}
	return resp.Body, contentType, nil
}

const propfindBody = `<?xml version="1.0" encoding="UTF-8"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
  <d:prop>
    <d:getlastmodified/>
    <d:getetag/>
    <d:getcontenttype/>
    <d:getcontentlength/>
    <d:resourcetype/>
    <oc:fileid/>
    <oc:size/>
    <oc:favorite/>
    <oc:share-types/>
    <d:displayname/>
  </d:prop>
</d:propfind>`
