package authentik

import (
	"context"
	"fmt"
	"net/url"
	"strings"
)

type akUser struct {
	PK         int            `json:"pk"`
	UUID       string         `json:"uuid"`
	Attributes map[string]any `json:"attributes"`
}

const avatarAttributeKey = "avatar"

// FindUserByUUID returns the Authentik core user for an OIDC subject (uuid).
func (c *Client) FindUserByUUID(ctx context.Context, userUUID string) (*akUser, bool, error) {
	userUUID = strings.TrimSpace(userUUID)
	if userUUID == "" {
		return nil, false, nil
	}
	q := url.Values{}
	q.Set("uuid", userUUID)
	var out listResponse[akUser]
	if err := c.getJSON(ctx, "/api/v3/core/users/?"+q.Encode(), &out); err != nil {
		return nil, false, err
	}
	if len(out.Results) == 0 {
		return nil, false, nil
	}
	user := out.Results[0]
	if user.Attributes == nil {
		user.Attributes = map[string]any{}
	}
	return &user, true, nil
}

// GetUserAvatarAttribute reads attributes.avatar from Authentik.
func (c *Client) GetUserAvatarAttribute(ctx context.Context, userUUID string) (string, error) {
	user, found, err := c.FindUserByUUID(ctx, userUUID)
	if err != nil || !found {
		return "", err
	}
	return avatarAttributeString(user.Attributes[avatarAttributeKey]), nil
}

// SetUserAvatarAttribute writes attributes.avatar on the Authentik user.
func (c *Client) SetUserAvatarAttribute(ctx context.Context, userUUID, avatarURL string) error {
	user, found, err := c.FindUserByUUID(ctx, userUUID)
	if err != nil {
		return err
	}
	if !found {
		return fmt.Errorf("authentik user not found: %s", userUUID)
	}
	attrs := cloneAttributes(user.Attributes)
	avatarURL = strings.TrimSpace(avatarURL)
	if avatarURL == "" {
		delete(attrs, avatarAttributeKey)
	} else {
		attrs[avatarAttributeKey] = avatarURL
	}
	return c.patchJSON(ctx, fmt.Sprintf("/api/v3/core/users/%d/", user.PK), map[string]any{
		"attributes": attrs,
	})
}

func cloneAttributes(src map[string]any) map[string]any {
	if len(src) == 0 {
		return map[string]any{}
	}
	dst := make(map[string]any, len(src))
	for k, v := range src {
		dst[k] = v
	}
	return dst
}

func avatarAttributeString(raw any) string {
	switch v := raw.(type) {
	case string:
		return strings.TrimSpace(v)
	case []byte:
		return strings.TrimSpace(string(v))
	default:
		if raw == nil {
			return ""
		}
		return strings.TrimSpace(fmt.Sprint(raw))
	}
}
