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

package bootstrap_test

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"github.com/mainflux/mainflux"
	"github.com/mainflux/mainflux/bootstrap"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

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

type readResp struct {
	MFThing    string     `json:"mainflux_id"`
	MFKey      string     `json:"mainflux_key"`
	MFChannels []readChan `json:"mainflux_channels"`
	Content    string     `json:"content,omitempty"`
	ClientCert string     `json:"client_cert,omitempty"`
	ClientKey  string     `json:"client_key,omitempty"`
	CACert     string     `json:"ca_cert,omitempty"`
}

func dec(in []byte) ([]byte, error) {
	block, err := aes.NewCipher(encKey)
	if err != nil {
		return nil, err
	}
	if len(in) < aes.BlockSize {
		return nil, bootstrap.ErrMalformedEntity
	}
	iv := in[:aes.BlockSize]
	in = in[aes.BlockSize:]
	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(in, in)
	return in, nil
}

func TestReadConfig(t *testing.T) {
	cfg := bootstrap.Config{
		MFThing:    "mf_id",
		ClientCert: "client_cert",
		ClientKey:  "client_key",
		CACert:     "ca_cert",
		MFKey:      "mf_key",
		MFChannels: []bootstrap.Channel{
			bootstrap.Channel{
				ID:       "mf_id",
				Name:     "mf_name",
				Metadata: map[string]interface{}{"key": "value}"},
			},
		},
		Content: "content",
	}
	ret := readResp{
		MFThing: "mf_id",
		MFKey:   "mf_key",
		MFChannels: []readChan{
			{
				ID:       "mf_id",
				Name:     "mf_name",
				Metadata: map[string]interface{}{"key": "value}"},
			},
		},
		Content:    "content",
		ClientCert: "client_cert",
		ClientKey:  "client_key",
		CACert:     "ca_cert",
	}

	bin, err := json.Marshal(ret)
	require.Nil(t, err, fmt.Sprintf("Marshalling expected to succeed: %s.\n", err))

	reader := bootstrap.NewConfigReader(encKey)
	cases := []struct {
		desc   string
		config bootstrap.Config
		enc    []byte
		secret bool
		err    error
	}{
		{
			desc:   "read a config",
			config: cfg,
			enc:    bin,
			secret: false,
		},
		{
			desc:   "read encrypted config",
			config: cfg,
			enc:    bin,
			secret: true,
		},
	}

	for _, tc := range cases {
		res, err := reader.ReadConfig(tc.config, tc.secret)
		require.Nil(t, err, fmt.Sprintf("Reading config to succeed: %s.\n", err))

		if tc.secret {
			d, err := dec(res.([]byte))
			require.Nil(t, err, fmt.Sprintf("Decrypting expected to succeed: %s.\n", err))
			assert.Equal(t, tc.enc, d, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.enc, d))
			continue
		}
		b, err := json.Marshal(res)
		require.Nil(t, err, fmt.Sprintf("Marshalling expected to succeed: %s.\n", err))
		assert.Equal(t, tc.enc, b, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.enc, b))
		resp, ok := res.(mainflux.Response)
		require.True(t, ok, fmt.Sprintf("If not encrypted, reader should return response."))
		assert.False(t, resp.Empty(), fmt.Sprintf("Response should not be empty %s.", err))
		assert.Equal(t, http.StatusOK, resp.Code(), fmt.Sprintf("Default config response code should be 200."))
	}
}
