package nextcloud

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

const groupFolderAllPermissions = 31

type GroupFolder struct {
	ID         int               `json:"id"`
	MountPoint string            `json:"mount_point"`
	Groups     map[string]int      `json:"groups"`
	Quota      int64             `json:"quota"`
	Size       int64             `json:"size"`
	ACL        bool              `json:"acl"`
	Manage     []string          `json:"manage"`
}

// GroupFolderWebDAVPath builds the WebDAV URL for a group folder root or subpath.
func GroupFolderWebDAVPath(folderID int, logicalPath string) string {
	base := fmt.Sprintf("/remote.php/dav/groupfolders/%d", folderID)
	logical := strings.Trim(logicalPath, "/")
	if logical == "" {
		return base
	}
	parts := strings.Split(logical, "/")
	for i, p := range parts {
		parts[i] = url.PathEscape(p)
	}
	return base + "/" + strings.Join(parts, "/")
}

func (c *Client) ListGroupFolders(ctx context.Context, userID string) ([]GroupFolder, error) {
	resp, err := c.DoAsUser(ctx, "GET", "/index.php/apps/groupfolders/folders?format=json", nil, userID, ocsJSONHeaders())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, &HTTPStatusError{Operation: "list groupfolders", StatusCode: resp.StatusCode}
	}
	return decodeGroupFoldersResponse(resp.Body)
}

func (c *Client) ListGroupFoldersAdmin(ctx context.Context) ([]GroupFolder, error) {
	resp, err := c.doRequest(ctx, "GET", "/index.php/apps/groupfolders/folders?format=json", nil, ocsJSONHeaders())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, &HTTPStatusError{Operation: "list groupfolders admin", StatusCode: resp.StatusCode}
	}
	return decodeGroupFoldersResponse(resp.Body)
}

func decodeGroupFoldersResponse(body io.Reader) ([]GroupFolder, error) {
	var payload struct {
		OCS struct {
			Data json.RawMessage `json:"data"`
		} `json:"ocs"`
	}
	if err := json.NewDecoder(body).Decode(&payload); err != nil {
		return nil, err
	}
	raw := payload.OCS.Data
	if len(raw) == 0 || string(raw) == "[]" || string(raw) == "null" {
		return nil, nil
	}

	var asMap map[string]GroupFolder
	if err := json.Unmarshal(raw, &asMap); err == nil && len(asMap) > 0 {
		out := make([]GroupFolder, 0, len(asMap))
		for _, item := range asMap {
			out = append(out, item)
		}
		return out, nil
	}

	var asList []GroupFolder
	if err := json.Unmarshal(raw, &asList); err != nil {
		return nil, err
	}
	return asList, nil
}

func (c *Client) CreateGroupFolder(ctx context.Context, mountPoint string) (int, error) {
	form := url.Values{}
	form.Set("mountpoint", mountPoint)
	resp, err := c.doRequest(ctx, "POST", "/index.php/apps/groupfolders/folders?format=json", strings.NewReader(form.Encode()), map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	})
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return 0, &HTTPStatusError{Operation: "create groupfolder", StatusCode: resp.StatusCode}
	}
	var payload struct {
		OCS struct {
			Data struct {
				ID int `json:"id"`
			} `json:"data"`
		} `json:"ocs"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
		return 0, err
	}
	if payload.OCS.Data.ID <= 0 {
		return 0, fmt.Errorf("groupfolder create returned empty id")
	}
	return payload.OCS.Data.ID, nil
}

func (c *Client) DeleteGroupFolder(ctx context.Context, folderID int) error {
	path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d?format=json", folderID)
	resp, err := c.doRequest(ctx, "DELETE", path, nil, ocsJSONHeaders())
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "delete groupfolder", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) AssignGroupToFolder(ctx context.Context, folderID int, groupID string, permissions int) error {
	if permissions <= 0 {
		permissions = groupFolderAllPermissions
	}
	form := url.Values{}
	form.Set("group", groupID)
	path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/groups?format=json", folderID)
	resp, err := c.doRequest(ctx, "POST", path, strings.NewReader(form.Encode()), map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "assign group to folder", StatusCode: resp.StatusCode}
	}

	formPerm := url.Values{}
	formPerm.Set("permissions", strconv.Itoa(permissions))
	pathPerm := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/groups/%s?format=json", folderID, url.PathEscape(groupID))
	resp2, err := c.doRequest(ctx, "POST", pathPerm, strings.NewReader(formPerm.Encode()), map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	})
	if err != nil {
		return err
	}
	defer resp2.Body.Close()
	if resp2.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "set groupfolder permissions", StatusCode: resp2.StatusCode}
	}
	return nil
}

func (c *Client) SetGroupFolderQuota(ctx context.Context, folderID int, quotaBytes int64) error {
	form := url.Values{}
	form.Set("quota", strconv.FormatInt(quotaBytes, 10))
	path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/quota?format=json", folderID)
	resp, err := c.doRequest(ctx, "POST", path, strings.NewReader(form.Encode()), map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "set groupfolder quota", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) RenameGroupFolder(ctx context.Context, folderID int, mountPoint string) error {
	form := url.Values{}
	form.Set("mountpoint", mountPoint)
	path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/mountpoint?format=json", folderID)
	resp, err := c.doRequest(ctx, "POST", path, strings.NewReader(form.Encode()), map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "rename groupfolder", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) EnsureGroup(ctx context.Context, groupID string) error {
	groupID = strings.TrimSpace(groupID)
	if groupID == "" {
		return fmt.Errorf("group id is empty")
	}
	exists, err := c.groupExists(ctx, groupID)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}
	form := url.Values{}
	form.Set("groupid", groupID)
	resp, err := c.doRequest(ctx, "POST", "/ocs/v1.php/cloud/groups", strings.NewReader(form.Encode()), map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return &HTTPStatusError{Operation: "create group", StatusCode: resp.StatusCode}
	}
	return nil
}

func (c *Client) groupExists(ctx context.Context, groupID string) (bool, error) {
	path := "/ocs/v1.php/cloud/groups/" + url.PathEscape(groupID)
	resp, err := c.doRequest(ctx, "GET", path, nil, ocsJSONHeaders())
	if err != nil {
		return false, err
	}
	defer resp.Body.Close()
	switch resp.StatusCode {
	case http.StatusOK:
		return true, nil
	case http.StatusNotFound:
		return false, nil
	default:
		return false, &HTTPStatusError{Operation: "group exists", StatusCode: resp.StatusCode}
	}
}

func OrgGroupID(orgSlug string) string {
	slug := strings.TrimSpace(strings.ToLower(orgSlug))
	slug = strings.ReplaceAll(slug, " ", "-")
	return "org-" + slug
}
