package prefixfile

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
)

type SlurmPrefixFilter struct {
	Prefix  string
	ASN     interface{}
	Comment string
}

func (pf *SlurmPrefixFilter) GetASN() (uint32, bool) {
	if pf.ASN == nil {
		return 0, true
	} else {
		switch asn := pf.ASN.(type) {
		case json.Number:
			c, _ := asn.Int64()
			return uint32(c), false
		case uint32:
			return asn, false
		default:
			return 0, true
		}
	}
}

func (pf *SlurmPrefixFilter) GetPrefix() *net.IPNet {
	_, prefix, _ := net.ParseCIDR(pf.Prefix)
	return prefix
}

type SlurmValidationOutputFilters struct {
	PrefixFilters []SlurmPrefixFilter
}

type SlurmPrefixAssertion struct {
	Prefix          string
	ASN             uint32
	MaxPrefixLength int
	Comment         string
}

func (pa *SlurmPrefixAssertion) GetASN() uint32 {
	return pa.ASN
}

func (pa *SlurmPrefixAssertion) GetPrefix() *net.IPNet {
	_, prefix, _ := net.ParseCIDR(pa.Prefix)
	return prefix
}

func (pa *SlurmPrefixAssertion) GetMaxLen() int {
	return pa.MaxPrefixLength
}

type SlurmLocallyAddedAssertions struct {
	PrefixAssertions []SlurmPrefixAssertion
}

type SlurmConfig struct {
	SlurmVersion            int
	ValidationOutputFilters SlurmValidationOutputFilters
	LocallyAddedAssertions  SlurmLocallyAddedAssertions
}

func DecodeJSONSlurm(buf io.Reader) (*SlurmConfig, error) {
	slurm := &SlurmConfig{}
	dec := json.NewDecoder(buf)
	dec.UseNumber()
	err := dec.Decode(slurm)
	if err != nil {
		return nil, err
	}
	return slurm, nil
}

func (s *SlurmValidationOutputFilters) FilterOnROAs(roas []ROAJson) ([]ROAJson, []ROAJson) {
	added := make([]ROAJson, 0)
	removed := make([]ROAJson, 0)
	if s.PrefixFilters == nil || len(s.PrefixFilters) == 0 {
		return roas, removed
	}
	for _, roa := range roas {
		rPrefix := roa.GetPrefix()
		var rIPStart net.IP
		var rIPEnd net.IP
		if rPrefix != nil {
			rIPStart = rPrefix.IP.To16()
			rIPEnd = GetIPBroadcast(*rPrefix).To16()
		}

		var wasRemoved bool
		for _, filter := range s.PrefixFilters {
			fPrefix := filter.GetPrefix()
			fASN, fASNEmpty := filter.GetASN()
			match := true
			if match && fPrefix != nil && rPrefix != nil {

				if !(fPrefix.Contains(rIPStart) && fPrefix.Contains(rIPEnd)) {
					match = false
				}
			}
			if match && !fASNEmpty {
				if roa.GetASN() != fASN {
					match = false
				}
			}
			if match {
				removed = append(removed, roa)
				wasRemoved = true
				break
			}
		}

		if !wasRemoved {
			added = append(added, roa)
		}
	}
	return added, removed
}

func (s *SlurmConfig) FilterOnROAs(roas []ROAJson) ([]ROAJson, []ROAJson) {
	return s.ValidationOutputFilters.FilterOnROAs(roas)
}

func (s *SlurmLocallyAddedAssertions) AssertROAs() []ROAJson {
	roas := make([]ROAJson, 0)
	if s.PrefixAssertions == nil || len(s.PrefixAssertions) == 0 {
		return roas
	}
	for _, assertion := range s.PrefixAssertions {
		prefix := assertion.GetPrefix()
		if prefix == nil {
			continue
		}
		size, _ := prefix.Mask.Size()
		maxLength := assertion.MaxPrefixLength
		if assertion.MaxPrefixLength <= size {
			maxLength = size
		}
		roas = append(roas, ROAJson{
			ASN:    fmt.Sprintf("AS%v", assertion.ASN),
			Prefix: assertion.Prefix,
			Length: uint8(maxLength),
			TA:     assertion.Comment,
		})
	}
	return roas
}

func (s *SlurmConfig) AssertROAs() []ROAJson {
	return s.LocallyAddedAssertions.AssertROAs()
}

func (s *SlurmConfig) FilterAssert(roas []ROAJson) []ROAJson {
	a, _ := s.FilterOnROAs(roas)
	b := s.AssertROAs()
	return append(a, b...)
}
