package nextcloud

import (
	"context"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
)

const propfindRevisionBody = `<?xml version="1.0" encoding="UTF-8"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
  <d:prop>
    <oc:fileid/>
    <d:getetag/>
  </d:prop>
</d:propfind>`

// FileRevision holds stable Nextcloud identifiers for a file (same across sharees).
type FileRevision struct {
	FileID int64
	ETag   string
}

func (c *Client) FileRevision(ctx context.Context, userID, filePath string) (FileRevision, error) {
	filePath = NormalizeClientFilePath(userID, filePath)
	davPath := c.WebDAVPath(userID, filePath)

	resp, err := c.DoAsUser(ctx, "PROPFIND", davPath, strings.NewReader(propfindRevisionBody), userID, map[string]string{
		"Depth":        "0",
		"Content-Type": "application/xml",
	})
	if err != nil {
		return FileRevision{}, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusMultiStatus && resp.StatusCode != http.StatusOK {
		return FileRevision{}, &HTTPStatusError{Operation: "propfind file revision", StatusCode: resp.StatusCode}
	}

	var ms struct {
		Responses []struct {
			Propstat struct {
				Prop struct {
					FileID string `xml:"http://owncloud.org/ns fileid"`
					ETag   string `xml:"getetag"`
				} `xml:"prop"`
			} `xml:"propstat"`
		} `xml:"response"`
	}
	if err := xml.NewDecoder(resp.Body).Decode(&ms); err != nil {
		return FileRevision{}, err
	}
	if len(ms.Responses) == 0 {
		return FileRevision{}, fmt.Errorf("file revision propfind: empty response")
	}

	raw := strings.TrimSpace(ms.Responses[0].Propstat.Prop.FileID)
	if raw == "" {
		return FileRevision{}, fmt.Errorf("file revision propfind: missing fileid")
	}
	id, err := strconv.ParseInt(raw, 10, 64)
	if err != nil {
		return FileRevision{}, fmt.Errorf("file revision propfind: invalid fileid %q", raw)
	}
	etag := strings.Trim(strings.TrimSpace(ms.Responses[0].Propstat.Prop.ETag), "\"")
	return FileRevision{FileID: id, ETag: etag}, nil
}

func (c *Client) FileID(ctx context.Context, userID, filePath string) (int64, error) {
	rev, err := c.FileRevision(ctx, userID, filePath)
	if err != nil {
		return 0, err
	}
	return rev.FileID, nil
}

func (c *Client) Preview(ctx context.Context, userID string, fileID int64, width, height int) (io.ReadCloser, string, error) {
	if width <= 0 {
		width = 400
	}
	if height <= 0 {
		height = 300
	}
	if width > 2048 {
		width = 2048
	}
	if height > 2048 {
		height = 2048
	}

	path := fmt.Sprintf(
		"/index.php/core/preview?fileId=%d&x=%d&y=%d&a=1&mode=cover&mimeFallback=true",
		fileID, width, height,
	)
	resp, err := c.DoAsUser(ctx, "GET", path, nil, userID, nil)
	if err != nil {
		return nil, "", err
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, "", &HTTPStatusError{Operation: "preview", StatusCode: resp.StatusCode}
	}

	contentType := resp.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "image/jpeg"
	}
	return resp.Body, contentType, nil
}
