package cloudflare

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/goccy/go-json"
)

// IPPrefix contains information about an IP prefix.
type IPPrefix struct {
	ID                   string     `json:"id"`
	CreatedAt            *time.Time `json:"created_at"`
	ModifiedAt           *time.Time `json:"modified_at"`
	CIDR                 string     `json:"cidr"`
	AccountID            string     `json:"account_id"`
	Description          string     `json:"description"`
	Approved             string     `json:"approved"`
	OnDemandEnabled      bool       `json:"on_demand_enabled"`
	OnDemandLocked       bool       `json:"on_demand_locked"`
	Advertised           bool       `json:"advertised"`
	AdvertisedModifiedAt *time.Time `json:"advertised_modified_at"`
}

// AdvertisementStatus contains information about the BGP status of an IP prefix.
type AdvertisementStatus struct {
	Advertised           bool       `json:"advertised"`
	AdvertisedModifiedAt *time.Time `json:"advertised_modified_at"`
}

// ListIPPrefixResponse contains a slice of IP prefixes.
type ListIPPrefixResponse struct {
	Response
	Result []IPPrefix `json:"result"`
}

// GetIPPrefixResponse contains a specific IP prefix's API Response.
type GetIPPrefixResponse struct {
	Response
	Result IPPrefix `json:"result"`
}

// GetAdvertisementStatusResponse contains an API Response for the BGP status of the IP Prefix.
type GetAdvertisementStatusResponse struct {
	Response
	Result AdvertisementStatus `json:"result"`
}

// IPPrefixUpdateRequest contains information about prefix updates.
type IPPrefixUpdateRequest struct {
	Description string `json:"description"`
}

// AdvertisementStatusUpdateRequest contains information about bgp status updates.
type AdvertisementStatusUpdateRequest struct {
	Advertised bool `json:"advertised"`
}

// ListPrefixes lists all IP prefixes for a given account
//
// API reference: https://api.cloudflare.com/#ip-address-management-prefixes-list-prefixes
func (api *API) ListPrefixes(ctx context.Context, accountID string) ([]IPPrefix, error) {
	uri := fmt.Sprintf("/accounts/%s/addressing/prefixes", accountID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []IPPrefix{}, err
	}

	result := ListIPPrefixResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return []IPPrefix{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
	}

	return result.Result, nil
}

// GetPrefix returns a specific IP prefix
//
// API reference: https://api.cloudflare.com/#ip-address-management-prefixes-prefix-details
func (api *API) GetPrefix(ctx context.Context, accountID, ID string) (IPPrefix, error) {
	uri := fmt.Sprintf("/accounts/%s/addressing/prefixes/%s", accountID, ID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return IPPrefix{}, err
	}

	result := GetIPPrefixResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return IPPrefix{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
	}

	return result.Result, nil
}

// UpdatePrefixDescription edits the description of the IP prefix
//
// API reference: https://api.cloudflare.com/#ip-address-management-prefixes-update-prefix-description
func (api *API) UpdatePrefixDescription(ctx context.Context, accountID, ID string, description string) (IPPrefix, error) {
	uri := fmt.Sprintf("/accounts/%s/addressing/prefixes/%s", accountID, ID)
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, IPPrefixUpdateRequest{Description: description})
	if err != nil {
		return IPPrefix{}, err
	}

	result := GetIPPrefixResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return IPPrefix{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
	}

	return result.Result, nil
}

// GetAdvertisementStatus returns the BGP status of the IP prefix
//
// API reference: https://api.cloudflare.com/#ip-address-management-prefixes-update-prefix-description
func (api *API) GetAdvertisementStatus(ctx context.Context, accountID, ID string) (AdvertisementStatus, error) {
	uri := fmt.Sprintf("/accounts/%s/addressing/prefixes/%s/bgp/status", accountID, ID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return AdvertisementStatus{}, err
	}

	result := GetAdvertisementStatusResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return AdvertisementStatus{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
	}

	return result.Result, nil
}

// UpdateAdvertisementStatus changes the BGP status of an IP prefix
//
// API reference: https://api.cloudflare.com/#ip-address-management-prefixes-update-prefix-description
func (api *API) UpdateAdvertisementStatus(ctx context.Context, accountID, ID string, advertised bool) (AdvertisementStatus, error) {
	uri := fmt.Sprintf("/accounts/%s/addressing/prefixes/%s/bgp/status", accountID, ID)
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, AdvertisementStatusUpdateRequest{Advertised: advertised})
	if err != nil {
		return AdvertisementStatus{}, err
	}

	result := GetAdvertisementStatusResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return AdvertisementStatus{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
	}

	return result.Result, nil
}
