package iprange

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
)

// AWS represents an AWS iprange
type AWS struct {
	ipNet   *net.IPNet
	region  string
	service string
}

// Contains returns if the ip is in this range
func (aws AWS) Contains(ip net.IP) bool {
	return aws.ipNet.Contains(ip)
}

// Description return a description of the ip net
func (aws AWS) Description() string {
	return fmt.Sprintf("%s<%s>", aws.service, aws.region)
}

// UnmarshalJSON implements json.Unmarshaller
func (aws *AWS) UnmarshalJSON(data []byte) error {
	var raw struct {
		Region  string `json:"region"`
		IPNet   string `json:"ip_prefix"`
		Service string `json:"service"`
	}

	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	_, ipNet, err := net.ParseCIDR(raw.IPNet)
	if err != nil {
		return err
	}

	aws.region = raw.Region
	aws.service = raw.Service
	aws.ipNet = ipNet

	return nil
}

// GetAWSIPRanges returns all AWS ip ranges
func GetAWSIPRanges(ctx context.Context) (out []AWS, err error) {
	res, err := http.Get("https://ip-ranges.amazonaws.com/ip-ranges.json")
	if err != nil {
		return nil, err
	}
	defer func() {
		closeErr := res.Body.Close()
		if err == nil {
			err = closeErr
		}
	}()

	switch res.StatusCode {
	case http.StatusOK:
	default:
		return nil, fmt.Errorf("invalid status: %s", res.Status)
	}

	var raw struct {
		Prefixes []AWS `json:"prefixes"`
	}

	if err := json.NewDecoder(res.Body).Decode(&raw); err != nil {
		return nil, err
	}

	return raw.Prefixes, nil
}
