/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package filter

import (
	"fmt"
	"testing"
)

import (
	"github.com/stretchr/testify/assert"
)

import (
	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
	"github.com/apache/dubbo-go-pixiu/pkg/logger"
	"github.com/apache/dubbo-go-pixiu/pkg/model"
)

const (
	DEMO = "dgp.filters.demo"
	// Kind is the kind of plugin.
	Kind = DEMO
)

func init() {
	RegisterHttpFilter(&Plugin{})
}

type (
	// Plugin is http filter plugin.
	Plugin struct {
	}
	// HeaderFilter is http filter instance
	DemoFilterFactory struct {
		conf *Config
	}
	DemoFilter struct {
		str string
	}
	// Config describe the config of ResponseFilter
	Config struct {
		Foo string `json:"foo,omitempty" yaml:"foo,omitempty"`
		Bar string `json:"bar,omitempty" yaml:"bar,omitempty"`
	}
)

func (p *Plugin) Kind() string {
	return Kind
}

func (p *Plugin) CreateFilterFactory() (HttpFilterFactory, error) {
	return &DemoFilterFactory{conf: &Config{Foo: "default foo", Bar: "default bar"}}, nil
}

func (f *DemoFilter) Decode(ctx *contexthttp.HttpContext) FilterStatus {
	logger.Info("decode phase: ", f.str)

	runes := []rune(f.str)
	for i := 0; i < len(runes)/2; i += 1 {
		runes[i], runes[len(runes)-1-i] = runes[len(runes)-1-i], runes[i]
	}
	f.str = string(runes)

	return Continue
}

func (f *DemoFilter) Encode(ctx *contexthttp.HttpContext) FilterStatus {
	logger.Info("encode phase: ", f.str)
	return Continue
}

func (f *DemoFilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain FilterChain) error {
	c := f.conf
	str := fmt.Sprintf("%s is drinking in the %s", c.Foo, c.Bar)
	filter := &DemoFilter{str: str}

	chain.AppendDecodeFilters(filter)
	chain.AppendEncodeFilters(filter)
	return nil
}

func (f *DemoFilterFactory) Config() any {
	return f.conf
}

func (f *DemoFilterFactory) Apply() error {
	return nil
}

func TestApply(t *testing.T) {
	fm := NewEmptyFilterManager()

	conf := map[string]any{}
	conf["foo"] = "Cat"
	conf["bar"] = "The Walnut"
	f, err := fm.Apply(DEMO, conf)
	assert.Nil(t, err)

	baseContext := &contexthttp.HttpContext{}
	chain := NewDefaultFilterChain()
	_ = f.PrepareFilterChain(baseContext, chain)
	chain.OnDecode(baseContext)
}

func TestLoad(t *testing.T) {
	fm := NewEmptyFilterManager()
	conf := map[string]any{}
	conf["foo"] = "Cat"
	conf["bar"] = "The Walnut"

	filtersConf := []*model.HTTPFilter{
		{
			Name:   DEMO,
			Config: conf,
		},
	}

	runFilter(t, fm, filtersConf)

	conf["foo"] = "Dog"
	conf["bar"] = "The Toilet"
	filtersConf = []*model.HTTPFilter{
		{
			Name:   DEMO,
			Config: conf,
		},
	}
	runFilter(t, fm, filtersConf)
}

func runFilter(t *testing.T, fm *FilterManager, filtersConf []*model.HTTPFilter) {
	fm.ReLoad(filtersConf)

	filters := fm.GetFactory()
	assert.Equal(t, len(filtersConf), len(filters))

	baseContext := &contexthttp.HttpContext{}
	baseContext.Reset()

	chain := fm.CreateFilterChain(baseContext)
	chain.OnDecode(baseContext)
	chain.OnEncode(baseContext)
}
