package sub2api

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

func (c *Client) ListManagedResources(ctx context.Context, req ListManagedResourcesRequest) (ManagedResourceSnapshot, error) {
	groups, err := c.listNamedResources(ctx, "/api/v1/admin/groups", req.GroupName)
	if err != nil {
		return ManagedResourceSnapshot{}, fmt.Errorf("list groups: %w", err)
	}
	channels, err := c.listNamedResources(ctx, "/api/v1/admin/channels", req.ChannelName)
	if err != nil {
		return ManagedResourceSnapshot{}, fmt.Errorf("list channels: %w", err)
	}
	plans, err := c.listNamedResources(ctx, "/api/v1/admin/payment/plans", req.PlanName)
	if err != nil {
		return ManagedResourceSnapshot{}, fmt.Errorf("list plans: %w", err)
	}
	accounts, err := c.listNamedResourcesPaged(ctx, "/api/v1/admin/accounts", 100)
	if err != nil {
		return ManagedResourceSnapshot{}, fmt.Errorf("list accounts: %w", err)
	}

	return ManagedResourceSnapshot{
		Groups:   groups,
		Channels: channels,
		Plans:    plans,
		Accounts: filterNamedResourcesByPrefix(accounts, req.AccountNamePrefix),
	}, nil
}

func (c *Client) listNamedResources(ctx context.Context, path, expectedName string) ([]NamedResource, error) {
	resources, _, err := c.listNamedResourcesPage(ctx, path)
	if err != nil {
		return nil, err
	}
	return filterNamedResourcesByName(resources, expectedName), nil
}

func (c *Client) listNamedResourcesPaged(ctx context.Context, path string, pageSize int) ([]NamedResource, error) {
	if pageSize <= 0 {
		pageSize = 100
	}
	page := 1
	all := make([]NamedResource, 0)
	for {
		query := url.Values{}
		query.Set("page", strconv.Itoa(page))
		query.Set("page_size", strconv.Itoa(pageSize))
		resources, pages, err := c.listNamedResourcesPage(ctx, path+"?"+query.Encode())
		if err != nil {
			return nil, err
		}
		all = append(all, resources...)
		if pages <= page || pages == 0 {
			return all, nil
		}
		page++
	}
}

func (c *Client) listNamedResourcesPage(ctx context.Context, path string) ([]NamedResource, int, error) {
	statusCode, _, body, err := c.perform(ctx, "GET", path, nil)
	if err != nil {
		return nil, 0, err
	}
	if statusCode < 200 || statusCode >= 300 {
		return nil, 0, newHTTPError("GET", path, statusCode, body)
	}

	resources, pages, err := decodeNamedResources(body)
	if err != nil {
		return nil, 0, fmt.Errorf("decode %s response: %w", path, err)
	}
	return resources, pages, nil
}

func decodeNamedResources(body []byte) ([]NamedResource, int, error) {
	var resources []NamedResource
	if err := decodeEnvelopeObject(body, &resources); err == nil {
		return resources, 1, nil
	}

	var wrapper struct {
		Data struct {
			Items []NamedResource `json:"items"`
			Pages int             `json:"pages"`
		} `json:"data"`
	}
	if err := json.Unmarshal(body, &wrapper); err != nil {
		return nil, 0, err
	}
	pages := wrapper.Data.Pages
	if pages <= 0 {
		pages = 1
	}
	return wrapper.Data.Items, pages, nil
}

func filterNamedResourcesByName(resources []NamedResource, expectedName string) []NamedResource {
	expectedName = strings.TrimSpace(expectedName)
	if expectedName == "" {
		return resources
	}

	filtered := make([]NamedResource, 0, len(resources))
	for _, resource := range resources {
		if strings.TrimSpace(resource.Name) == expectedName {
			filtered = append(filtered, resource)
		}
	}
	return filtered
}

func filterNamedResourcesByPrefix(resources []NamedResource, prefix string) []NamedResource {
	prefix = strings.TrimSpace(prefix)
	if prefix == "" {
		return resources
	}

	filtered := make([]NamedResource, 0, len(resources))
	for _, resource := range resources {
		if strings.HasPrefix(strings.TrimSpace(resource.Name), prefix) {
			filtered = append(filtered, resource)
		}
	}
	return filtered
}
