package api_test

import (
	"bytes"
	"database/sql"
	"fmt"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	mockdb "git.nochill.in/nochill/hiling_go/db/mock"
	db "git.nochill.in/nochill/hiling_go/db/sqlc"
	"git.nochill.in/nochill/hiling_go/util"
	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
)

func TestGetLocationsAPI(t *testing.T) {
	location := randomLocation()

	testCases := []struct {
		name          string
		id            int32
		buildStubs    func(store *mockdb.MockStore)
		checkResponse func(t *testing.T, recorder *httptest.ResponseRecorder)
	}{
		{
			name: "OK",
			id:   location.ID,
			buildStubs: func(store *mockdb.MockStore) {
				store.EXPECT().
					GetLocation(gomock.Any(), gomock.Eq(location.ID)).
					Times(1).
					Return(location, nil)
			},
			checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) {
				require.Equal(t, http.StatusOK, recorder.Code)
			},
		},
	}

	for i := range testCases {
		tc := testCases[i]
		t.Run(tc.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()

			store := mockdb.NewMockStore(ctrl)
			tc.buildStubs(store)

			server := newTestServer(t, store)
			recorder := httptest.NewRecorder()

			url := fmt.Sprintf("/location/%d", tc.id)
			request, err := http.NewRequest(http.MethodGet, url, nil)
			require.NoError(t, err)

			server.Router.ServeHTTP(recorder, request)
			tc.checkResponse(t, recorder)
		})
	}

}

func TestCreateLocationAPI(t *testing.T) {
	location := db.CreateLocationParams{
		Address:        util.RandomString(10),
		Name:           util.RandomString(10),
		SubmittedBy:    1,
		RegencyID:      1305,
		GoogleMapsLink: sql.NullString{Valid: true, String: util.RandomString(10)},
	}

	testCases := []struct {
		name          string
		body          db.CreateLocationParams
		buildStubs    func(store *mockdb.MockStore)
		checkResponse func(t *testing.T, recorder *httptest.ResponseRecorder)
	}{
		{
			name: "OK",
			body: location,
			buildStubs: func(store *mockdb.MockStore) {
				arg := db.CreateLocationParams{
					Address:        location.Address,
					Name:           location.Name,
					SubmittedBy:    location.SubmittedBy,
					RegencyID:      location.RegencyID,
					GoogleMapsLink: location.GoogleMapsLink,
				}
				store.EXPECT().
					CreateLocation(gomock.Any(), gomock.Eq(arg)).
					Times(1).
					Return(nil)
			},
			checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) {
				require.Equal(t, http.StatusOK, recorder.Code)
			},
		},
		{
			name: "Bad Request",
			body: db.CreateLocationParams{},
			buildStubs: func(store *mockdb.MockStore) {
				store.EXPECT().
					CreateLocation(gomock.Any(), gomock.Any()).
					Times(0)
			},
			checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) {
				require.Equal(t, http.StatusBadRequest, recorder.Code)
			},
		},
	}

	for i := range testCases {
		tc := testCases[i]

		t.Run(tc.name, func(t *testing.T) {
			bodyBuffer := new(bytes.Buffer)
			mw := multipart.NewWriter(bodyBuffer)
			mw.WriteField("address", tc.body.Address)
			mw.WriteField("name", tc.body.Name)
			mw.WriteField("submitted_by", fmt.Sprintf("%d", tc.body.SubmittedBy))
			mw.WriteField("regency_id", fmt.Sprintf("%d", tc.body.RegencyID))
			mw.WriteField("google_maps_link", tc.body.GoogleMapsLink.String)
			mw.Close()

			ctrl := gomock.NewController(t)
			defer ctrl.Finish()

			store := mockdb.NewMockStore(ctrl)
			tc.buildStubs(store)

			server := newTestServer(t, store)
			recorder := httptest.NewRecorder()

			url := "/locations"
			request, err := http.NewRequest(http.MethodPost, url, bodyBuffer)
			request.Header.Set("Content-Type", mw.FormDataContentType())
			require.NoError(t, err)

			server.Router.ServeHTTP(recorder, request)
			tc.checkResponse(t, recorder)

		})
	}
}

func randomLocation() db.Location {
	return db.Location{
		ID:             int32(util.RandomInt(1, 20)),
		Address:        util.RandomString(10),
		Name:           util.RandomString(10),
		GoogleMapsLink: sql.NullString{Valid: true, String: util.RandomString(20)},
		SubmittedBy:    1,
		TotalVisited:   sql.NullInt32{Valid: true, Int32: int32(util.RandomInt(0, 32))},
		Thumbnail:      sql.NullString{Valid: false, String: ""},
		RegencyID:      1305,
		IsDeleted:      sql.NullBool{Valid: true, Bool: false},
		CreatedAt:      sql.NullTime{Valid: true, Time: time.Now()},
		UpdatedAt:      sql.NullTime{Valid: true, Time: time.Now()},
		ApprovedBy:     sql.NullInt32{Valid: true, Int32: 1},
		ApprovedAt:     sql.NullTime{Valid: true, Time: time.Now()},
	}
}