package handler

import (
	"database/sql"
	"errors"
	"net/http/httptest"
	"testing"

	"github.com/brianvoe/gofakeit/v6"
	"github.com/gofiber/fiber/v2"
	"github.com/gojuno/minimock/v3"
	"github.com/stretchr/testify/assert"

	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helper/test"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/model"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/service/handler/mocks"
)

func TestMainPageHandler(t *testing.T) {
	t.Parallel()

	type req struct {
		method string
		route  string
	}

	var (
		publishTime = gofakeit.Date()
		internalErr = errors.New(gofakeit.Phrase())

		previewArticles = []model.ArticlePreview{
			{
				ID:          gofakeit.Uint64(),
				URL:         gofakeit.URL(),
				Title:       gofakeit.Phrase(),
				PublishTime: publishTime,
				PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
				Image:       gofakeit.URL(),
			},
			{
				ID:          gofakeit.Uint64(),
				URL:         gofakeit.URL(),
				Title:       gofakeit.Phrase(),
				PublishTime: publishTime,
				PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
				Image:       gofakeit.URL(),
			},
			{
				ID:          gofakeit.Uint64(),
				URL:         gofakeit.URL(),
				Title:       gofakeit.Phrase(),
				PublishTime: publishTime,
				PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
				Image:       gofakeit.URL(),
			},
		}
	)

	tests := []struct {
		name        string
		req         req
		res         int
		err         error
		cacheMock   func(mc *minimock.Controller) CacheService
		articleMock func(mc *minimock.Controller) ArticleRepository
	}{
		{
			name: "positive case",
			req: req{
				method: fiber.MethodGet,
				route:  "/",
			},
			res: fiber.StatusOK,
			err: nil,
			cacheMock: func(mc *minimock.Controller) CacheService {
				mock := mocks.NewCacheServiceMock(mc)
				mock.GetMock.Return(nil, false)
				mock.SetMock.Return()
				return mock
			},
			articleMock: func(mc *minimock.Controller) ArticleRepository {
				mock := mocks.NewArticleRepositoryMock(mc)
				mock.GetAllPreviewMock.Return(previewArticles, nil)
				return mock
			},
		},
		{
			name: "negative case - article repository error",
			req: req{
				method: fiber.MethodGet,
				route:  "/",
			},
			res: fiber.StatusInternalServerError,
			err: nil,
			cacheMock: func(mc *minimock.Controller) CacheService {
				mock := mocks.NewCacheServiceMock(mc)
				mock.GetMock.Return(nil, false)
				return mock
			},
			articleMock: func(mc *minimock.Controller) ArticleRepository {
				mock := mocks.NewArticleRepositoryMock(mc)
				mock.GetAllPreviewMock.Return(nil, internalErr)
				return mock
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			mc := minimock.NewController(t)
			fiberApp := fiber.New(test.GetFiberTestConfig())
			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)

			fiberApp.Get("/", NewMainPageHandler(
				tt.cacheMock(mc),
				tt.articleMock(mc),
			))

			fiberRes, fiberErr := fiberApp.Test(fiberReq)
			assert.Equal(t, tt.res, fiberRes.StatusCode)
			assert.Equal(t, tt.err, fiberErr)
		})
	}
}
