// Copyright (c) 2022 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package appservice

import (
	"io/ioutil"
	"regexp"

	"gopkg.in/yaml.v3"

	"maunium.net/go/mautrix/util"
)

// Registration contains the data in a Matrix appservice registration.
// See https://spec.matrix.org/v1.2/application-service-api/#registration
type Registration struct {
	ID              string     `yaml:"id" json:"id"`
	URL             string     `yaml:"url" json:"url"`
	AppToken        string     `yaml:"as_token" json:"as_token"`
	ServerToken     string     `yaml:"hs_token" json:"hs_token"`
	SenderLocalpart string     `yaml:"sender_localpart" json:"sender_localpart"`
	RateLimited     *bool      `yaml:"rate_limited,omitempty" json:"rate_limited,omitempty"`
	Namespaces      Namespaces `yaml:"namespaces" json:"namespaces"`
	Protocols       []string   `yaml:"protocols,omitempty" json:"protocols,omitempty"`

	SoruEphemeralEvents bool `yaml:"de.sorunome.msc2409.push_ephemeral,omitempty" json:"de.sorunome.msc2409.push_ephemeral,omitempty"`
	EphemeralEvents     bool `yaml:"push_ephemeral,omitempty" json:"push_ephemeral,omitempty"`
}

// CreateRegistration creates a Registration with random appservice and homeserver tokens.
func CreateRegistration() *Registration {
	return &Registration{
		AppToken:    util.RandomString(64),
		ServerToken: util.RandomString(64),
	}
}

// LoadRegistration loads a YAML file and turns it into a Registration.
func LoadRegistration(path string) (*Registration, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	reg := &Registration{}
	err = yaml.Unmarshal(data, reg)
	if err != nil {
		return nil, err
	}
	return reg, nil
}

// Save saves this Registration into a file at the given path.
func (reg *Registration) Save(path string) error {
	data, err := yaml.Marshal(reg)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path, data, 0600)
}

// YAML returns the registration in YAML format.
func (reg *Registration) YAML() (string, error) {
	data, err := yaml.Marshal(reg)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

// Namespaces contains the three areas that appservices can reserve parts of.
type Namespaces struct {
	UserIDs     NamespaceList `yaml:"users,omitempty" json:"users,omitempty"`
	RoomAliases NamespaceList `yaml:"aliases,omitempty" json:"aliases,omitempty"`
	RoomIDs     NamespaceList `yaml:"rooms,omitempty" json:"rooms,omitempty"`
}

// Namespace is a reserved namespace in any area.
type Namespace struct {
	Regex     string `yaml:"regex" json:"regex"`
	Exclusive bool   `yaml:"exclusive" json:"exclusive"`
}

type NamespaceList []Namespace

func (nsl *NamespaceList) Register(regex *regexp.Regexp, exclusive bool) {
	ns := Namespace{
		Regex:     regex.String(),
		Exclusive: exclusive,
	}
	if nsl == nil {
		*nsl = []Namespace{ns}
	} else {
		*nsl = append(*nsl, ns)
	}
}
