// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// Copyright twenty-panda <twenty-panda@posteo.com>
// SPDX-License-Identifier: MIT

package hoverfly

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"testing"

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

type testNothing struct{}

func (o testNothing) Run() (code int) {
	return 456
}

type testSomething struct {
	t *testing.T
}

func (o *testSomething) Run() (code int) {
	return 123
}

type testSimulate struct {
	t *testing.T
}

func (o testSimulate) Run() (code int) {
	return 789
}

func verifyRequest(t *testing.T, serverURL, payload string) {
	t.Helper()

	proxyString, ok := os.LookupEnv("http_proxy")
	require.True(t, ok)

	proxyURL, err := url.Parse(proxyString)
	require.NoError(t, err)

	client := http.Client{}
	client.Transport = &http.Transport{
		Proxy: func(req *http.Request) (*url.URL, error) {
			return http.ProxyURL(proxyURL)(req)
		},
	}

	res, err := client.Get(serverURL)
	require.NoError(t, err)
	answer, err := io.ReadAll(res.Body)
	res.Body.Close()
	require.NoError(t, err)
	assert.Equal(t, payload, string(answer))
}

func runServer(t *testing.T, payload string) (string, func()) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, payload)
	}))
	require.NotNil(t, ts)
	return ts.URL, ts.Close
}

func TestHoverfly(t *testing.T) {
	hoverfly := newHoverfly()
	var exitCode int
	exit = func(code int) {
		exitCode = code
	}
	saveDir := t.TempDir()
	getwd = func() (string, error) {
		return saveDir, nil
	}
	hoverfly.SetOutput(os.Stderr)

	// do not run hoverfly
	os.Unsetenv("HOVERFLY")
	hoverfly.Run(testNothing{})
	assert.Equal(t, 456, exitCode)

	// run hoverfly and set the proxy
	os.Setenv("HOVERFLY", modeCapture)
	something := testSomething{t: t}
	hoverfly.Run(&something)
	assert.Equal(t, 123, exitCode)

	testname := "thetest"
	greeting := "Hello world"
	serverURL, shutdownServer := runServer(t, greeting)
	t.Run("capture requests and save them", func(t *testing.T) {
		os.Setenv("HOVERFLY", modeCapture)
		output := bytes.NewBuffer([]byte{})
		hoverflySingleton.SetOutput(output)
		require.NoError(t, hoverflySingleton.setup())

		cleanup := Run(t, testname)
		assert.Contains(t, output.String(), "capture mode")

		verifyRequest(t, serverURL, greeting)

		cleanup()
		simulationPath, err := hoverflySingleton.getSimulationPath(testname)
		require.NoError(t, err)
		simulation, err := os.ReadFile(simulationPath)
		require.NoError(t, err)
		assert.Contains(t, string(simulation), greeting)

		require.NoError(t, hoverflySingleton.teardown())
	})

	shutdownServer()

	t.Run("read saved request and simulate them", func(t *testing.T) {
		os.Setenv("HOVERFLY", modeSimulate)
		output := bytes.NewBuffer([]byte{})
		hoverflySingleton.SetOutput(output)
		require.NoError(t, hoverflySingleton.setup())
		cleanup := Run(t, testname)
		assert.Contains(t, output.String(), "simulate mode")

		verifyRequest(t, serverURL, greeting)

		cleanup()

		require.NoError(t, hoverflySingleton.teardown())
	})
}
