func (p *ProfileId) IsOverlappingWith(other *ProfileId) bool {
  return p.IsValid(other.FirstSeenAt) || p.IsValid(other.ValidUntil) || (p.Id == other.Id && p.ValidUntil.Add(NameChangeRateLimitPeriod).After(p.FirstSeenAt))
}
evaluates whether two profileIds theoretically overlap

two profiles are considered to overlap if their validity period overlaps at any point in time or
if their assignments are equal while less than 30 days have passed (e.g. it is impossible for
another user to claim and unclaim the name in the meantime due to the grace period)
TODO: I have no clue how and whether Mojang handles theft of names with content creators
package entity
import (
  "encoding/json"
  "io"
  "time"
  "github.com/google/uuid"
)
type ProfileId struct {
  Id          uuid.UUID
  Name        string
  FirstSeenAt time.Time
  LastSeenAt  time.Time
  ValidUntil  time.Time
}
type restProfileId struct {
  Id   string `json:"id"`
  Name string `json:"name"`
}
type serializableProfileId struct {
  restProfileId
  FirstSeenAt int64 `json:"firstSeenAt"`
  LastSeenAt  int64 `json:"lastSeenAt"`
  ValidUntil  int64 `json:"validUntil"`
}
func (p *ProfileId) Serialize() ([]byte, error) {
  enc := serializableProfileId{
    restProfileId: restProfileId{
      Id:   p.Id.String(),
      Name: p.Name,
    },
    FirstSeenAt: p.FirstSeenAt.Unix(),
    LastSeenAt:  p.LastSeenAt.Unix(),
    ValidUntil:  p.ValidUntil.Unix(),
  }
  return json.Marshal(&enc)
}
func SerializeProfileIdArray(profileIds []*ProfileId) ([]byte, error) {
  enc := make([]serializableProfileId, len(profileIds))
  for i, profileId := range profileIds {
    enc[i] = serializableProfileId{
      restProfileId: restProfileId{
        Id:   profileId.Id.String(),
        Name: profileId.Name,
      },
      FirstSeenAt: profileId.FirstSeenAt.Unix(),
      LastSeenAt:  profileId.LastSeenAt.Unix(),
      ValidUntil:  profileId.ValidUntil.Unix(),
    }
  }
  return json.Marshal(&enc)
}
func (p *ProfileId) Deserialize(enc []byte) error {
  parsed := serializableProfileId{}
  err := json.Unmarshal(enc, &parsed)
  if err != nil {
    return err
  }
  id, err := uuid.Parse(parsed.Id)
  if err != nil {
    return err
  }
  p.Id = id
  p.Name = parsed.Name
  p.FirstSeenAt = time.Unix(parsed.FirstSeenAt, 0)
  p.LastSeenAt = time.Unix(parsed.LastSeenAt, 0)
  p.ValidUntil = time.Unix(parsed.ValidUntil, 0)
  return nil
}
func DeserializeProfileIdArray(enc []byte) ([]*ProfileId, error) {
  parsed := make([]serializableProfileId, 0)
  err := json.Unmarshal(enc, &parsed)
  if err != nil {
    return nil, err
  }
  res := make([]*ProfileId, len(parsed))
  for i, profileId := range parsed {
    id, err := uuid.Parse(profileId.Id)
    if err != nil {
      return nil, err
    }
    res[i] = &ProfileId{
      Id:          id,
      Name:        profileId.Name,
      FirstSeenAt: time.Unix(profileId.FirstSeenAt, 0),
      LastSeenAt:  time.Unix(profileId.LastSeenAt, 0),
      ValidUntil:  time.Unix(profileId.ValidUntil, 0),
    }
  }
  return res, nil
}
func (p *ProfileId) Read(reader io.Reader, at time.Time) error {
  parsed := restProfileId{}
  err := json.NewDecoder(reader).Decode(&parsed)
  if err != nil {
    return err
  }
  id, err := ParseId(parsed.Id)
  if err != nil {
    return err
  }
  p.Id = id
  p.Name = parsed.Name
  p.FirstSeenAt = at
  p.LastSeenAt = at
  p.ValidUntil = CalculateNameGracePeriodEnd(at)
  return nil
}
func ReadProfileIdArray(reader io.Reader) ([]*ProfileId, error) {
  parsed := make([]restProfileId, 0)
  err := json.NewDecoder(reader).Decode(&parsed)
  if err != nil {
    return nil, err
  }
  at := time.Now()
  res := make([]*ProfileId, len(parsed))
  for i, profileId := range parsed {
    id, err := ParseId(profileId.Id)
    if err != nil {
      return nil, err
    }
    res[i] = &ProfileId{
      Id:          id,
      Name:        profileId.Name,
      FirstSeenAt: at,
      LastSeenAt:  at,
      ValidUntil:  CalculateNameGracePeriodEnd(at),
    }
  }
  return res, nil
}
func (p *ProfileId) UpdateDiscovery(at time.Time) {
  p.FirstSeenAt = at
  p.LastSeenAt = at
  p.ValidUntil = CalculateNameGracePeriodEnd(at)
}
func (p *ProfileId) UpdateExpiration(seen time.Time) {
  if p.LastSeenAt.After(seen) {
    return  
  }
  p.LastSeenAt = seen
  p.ValidUntil = CalculateNameGracePeriodEnd(seen)
}
func (p *ProfileId) IsValid(at time.Time) bool {
  return !p.FirstSeenAt.After(at) && p.ValidUntil.After(at)
}