package main

import (
	"bytes"
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"go-nkode/core/api"
	m "go-nkode/core/model"
	"io"
	"net/http"
	"testing"
)

func TestApi(t *testing.T) {
	base := "http://localhost:8080"
	newCustomerBody := m.NewCustomerPost{
		NKodePolicy: m.NewDefaultNKodePolicy(),
	}
	kp := m.KeypadDimension{
		AttrsPerKey: 14,
		NumbOfKeys:  10,
	}
	var customerResp m.CreateNewCustomerResp
	testApiCall(t, base+api.CreateNewCustomer, newCustomerBody, &customerResp)

	username := m.Username("test_username")
	signupInterfaceBody := m.GenerateSignupInterfacePost{
		CustomerId:  customerResp.CustomerId,
		AttrsPerKey: kp.AttrsPerKey,
		NumbOfKeys:  kp.NumbOfKeys,
		Username:    username,
	}
	var signupInterfaceResp m.GenerateSignupInterfaceResp
	testApiCall(t, base+api.GenerateSignupInterface, signupInterfaceBody, &signupInterfaceResp)

	passcodeLen := 4
	setInterface := signupInterfaceResp.UserInterface
	userPasscode := setInterface[:passcodeLen]
	kp_set := m.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys}
	setKeySelection, err := m.SelectKeyByAttrIdx(setInterface, userPasscode, kp_set)
	assert.NoError(t, err)
	setNKodeBody := m.SetNKodePost{
		CustomerId:   customerResp.CustomerId,
		SessionId:    signupInterfaceResp.SessionId,
		KeySelection: setKeySelection,
	}
	var setNKodeResp m.SetNKodeResp
	testApiCall(t, base+api.SetNKode, setNKodeBody, &setNKodeResp)
	confirmInterface := setNKodeResp.UserInterface
	confirmKeySelection, err := m.SelectKeyByAttrIdx(confirmInterface, userPasscode, kp_set)
	assert.NoError(t, err)
	confirmNKodeBody := m.ConfirmNKodePost{
		CustomerId:   customerResp.CustomerId,
		KeySelection: confirmKeySelection,
		SessionId:    signupInterfaceResp.SessionId,
	}
	testApiCall(t, base+api.ConfirmNKode, confirmNKodeBody, nil)

	loginInterfaceBody := m.GetLoginInterfacePost{
		CustomerId: customerResp.CustomerId,
		Username:   username,
	}

	var loginInterfaceResp m.GetLoginInterfaceResp
	testApiCall(t, base+api.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp)

	loginKeySelection, err := m.SelectKeyByAttrIdx(loginInterfaceResp.UserInterface, userPasscode, kp)
	assert.NoError(t, err)
	loginBody := m.LoginPost{
		CustomerId:   customerResp.CustomerId,
		Username:     username,
		KeySelection: loginKeySelection,
	}

	testApiCall(t, base+api.Login, loginBody, nil)

	renewBody := m.RenewAttributesPost{CustomerId: customerResp.CustomerId}
	testApiCall(t, base+api.RenewAttributes, renewBody, nil)

	loginKeySelection, err = m.SelectKeyByAttrIdx(loginInterfaceResp.UserInterface, userPasscode, kp)
	assert.NoError(t, err)
	loginBody = m.LoginPost{
		CustomerId:   customerResp.CustomerId,
		Username:     username,
		KeySelection: loginKeySelection,
	}

	testApiCall(t, base+api.Login, loginBody, nil)
}

func Unmarshal(t *testing.T, resp *http.Response, data any) {
	responseBody, err := io.ReadAll(resp.Body)
	assert.NoError(t, err)
	err = json.Unmarshal(responseBody, data)
	assert.NoError(t, err)
}

func Marshal(t *testing.T, data any) *bytes.Reader {
	jsonBytes, err := json.Marshal(data)
	assert.NoError(t, err)
	reader := bytes.NewReader(jsonBytes)
	return reader
}

func testApiCall(t *testing.T, endpointStr string, postBody any, respBody any) {
	reader := Marshal(t, postBody)
	resp, err := http.Post(endpointStr, "application/json", reader)
	assert.NoError(t, err)
	assert.Equal(t, resp.StatusCode, http.StatusOK)
	if respBody != nil {
		Unmarshal(t, resp, respBody)
	}
}
