package controller

import (
	"net/http/httptest"
	"os"
	"path/filepath"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/tinyauthapp/tinyauth/internal/model"
	"github.com/tinyauthapp/tinyauth/internal/test"
)

func TestResourcesController(t *testing.T) {
	cfg, _ := test.CreateTestConfigs(t)

	err := os.MkdirAll(cfg.Resources.Path, 0777)
	require.NoError(t, err)

	// create a "backup" of the original configuration to restore after each test
	originalCfg := cfg.Resources

	type testCase struct {
		description string
		customCfg   *model.ResourcesConfig
		run         func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder)
	}

	tests := []testCase{
		{
			description: "Ensure resources endpoint returns 200 OK for existing file",
			run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
				req := httptest.NewRequest("GET", "/resources/testfile.txt", nil)
				router.ServeHTTP(recorder, req)

				assert.Equal(t, 200, recorder.Code)
				assert.Equal(t, "This is a test file.", recorder.Body.String())
			},
		},
		{
			description: "Ensure resources endpoint returns 404 Not Found for non-existing file",
			run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
				req := httptest.NewRequest("GET", "/resources/nonexistent.txt", nil)
				router.ServeHTTP(recorder, req)

				assert.Equal(t, 404, recorder.Code)
			},
		},
		{
			description: "Ensure resources controller denies path traversal",
			run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
				req := httptest.NewRequest("GET", "/resources/../somefile.txt", nil)
				router.ServeHTTP(recorder, req)

				assert.Equal(t, 404, recorder.Code)
			},
		},
		{
			description: "Ensure resources controller returns 404 when resources path is empty",
			customCfg: &model.ResourcesConfig{
				Path:    "",
				Enabled: true,
			},
			run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
				req := httptest.NewRequest("GET", "/resources/testfile.txt", nil)
				router.ServeHTTP(recorder, req)

				assert.Equal(t, 404, recorder.Code)
			},
		},
		{
			description: "Ensure resources controller returns 403 when resources are disabled",
			customCfg: &model.ResourcesConfig{
				Path:    cfg.Resources.Path,
				Enabled: false,
			},
			run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
				req := httptest.NewRequest("GET", "/resources/testfile.txt", nil)
				router.ServeHTTP(recorder, req)

				assert.Equal(t, 403, recorder.Code)
			},
		},
	}

	testFilePath := cfg.Resources.Path + "/testfile.txt"
	err = os.WriteFile(testFilePath, []byte("This is a test file."), 0777)
	require.NoError(t, err)

	testFilePathParent := filepath.Dir(cfg.Resources.Path) + "/somefile.txt"
	err = os.WriteFile(testFilePathParent, []byte("This file should not be accessible."), 0777)
	require.NoError(t, err)

	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			router := gin.Default()
			group := router.Group("/")
			gin.SetMode(gin.TestMode)

			// if custom configuration is provided, override the default config
			if test.customCfg != nil {
				cfg.Resources = *test.customCfg
			} else {
				// Reset to default configuration for each test
				cfg.Resources = originalCfg
			}

			NewResourcesController(ResourcesControllerInput{
				RouterGroup: group,
				Config:      &cfg,
			})

			recorder := httptest.NewRecorder()
			test.run(t, router, recorder)
		})
	}
}
