// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0

package bootstrap

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/json"
	"io"
	"net/http"
)

// bootstrapRes represent SuperMQ Response to the Bootatrap request.
// This is used as a response from ConfigReader and can easily be
// replace with any other response format.
type bootstrapRes struct {
	ClientID     string       `json:"client_id"`
	ClientSecret string       `json:"client_secret"`
	Channels     []channelRes `json:"channels"`
	Content      string       `json:"content,omitempty"`
	ClientCert   string       `json:"client_cert,omitempty"`
	ClientKey    string       `json:"client_key,omitempty"`
	CACert       string       `json:"ca_cert,omitempty"`
}

type channelRes struct {
	ID       string `json:"id"`
	Name     string `json:"name,omitempty"`
	Metadata any    `json:"metadata,omitempty"`
}

func (res bootstrapRes) Code() int {
	return http.StatusOK
}

func (res bootstrapRes) Headers() map[string]string {
	return map[string]string{}
}

func (res bootstrapRes) Empty() bool {
	return false
}

type reader struct {
	encKey []byte
}

// NewConfigReader return new reader which is used to generate response
// from the config.
func NewConfigReader(encKey []byte) ConfigReader {
	return reader{encKey: encKey}
}

func (r reader) ReadConfig(cfg Config, secure bool) (any, error) {
	var channels []channelRes
	for _, ch := range cfg.Channels {
		channels = append(channels, channelRes{ID: ch.ID, Name: ch.Name, Metadata: ch.Metadata})
	}

	res := bootstrapRes{
		ClientID:     cfg.ClientID,
		ClientSecret: cfg.ClientSecret,
		Channels:     channels,
		Content:      cfg.Content,
		ClientCert:   cfg.ClientCert,
		ClientKey:    cfg.ClientKey,
		CACert:       cfg.CACert,
	}
	if secure {
		b, err := json.Marshal(res)
		if err != nil {
			return nil, err
		}
		return r.encrypt(b)
	}

	return res, nil
}

func (r reader) encrypt(in []byte) ([]byte, error) {
	block, err := aes.NewCipher(r.encKey)
	if err != nil {
		return nil, err
	}
	ciphertext := make([]byte, aes.BlockSize+len(in))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], in)
	return ciphertext, nil
}
