package controllers

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"GoMembership/internal/config"
	"GoMembership/internal/models"
	"GoMembership/pkg/logger"

	"github.com/gin-gonic/gin"
)

type RegisterSubscriptionTest struct {
	WantDBData   map[string]interface{}
	Input        string
	Name         string
	WantResponse int
	Assert       bool
}

func testMembershipController(t *testing.T) {

	tests := getSubscriptionData()
	for _, tt := range tests {
		logger.Error.Print("==============================================================")
		logger.Error.Printf("MembershipController : %v", tt.Name)
		logger.Error.Print("==============================================================")
		t.Run(tt.Name, func(t *testing.T) {
			if err := runSingleTest(&tt); err != nil {
				t.Errorf("Test failed: %v", err.Error())
			}
		})
	}
}

func (rt *RegisterSubscriptionTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder, *gin.Engine) {
	return GetMockedJSONContext([]byte(rt.Input), "api/subscription")
}

func (rt *RegisterSubscriptionTest) RunHandler(c *gin.Context, router *gin.Engine) {
	Mc.RegisterSubscription(c)
}

func (rt *RegisterSubscriptionTest) ValidateResponse(w *httptest.ResponseRecorder) error {
	if w.Code != rt.WantResponse {
		return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, rt.WantResponse)
	}
	return nil
}

func (rt *RegisterSubscriptionTest) ValidateResult() error {
	return validateSubscription(rt.Assert, rt.WantDBData)
}

func validateSubscription(assert bool, wantDBData map[string]interface{}) error {
	subscriptions, err := Mc.Service.GetSubscriptions(wantDBData)
	if err != nil {
		return fmt.Errorf("Error in database ops: %#v", err)
	}
	if assert != (len(*subscriptions) != 0) {
		return fmt.Errorf("Subscription entry query didn't met expectation: %v != %#v", assert, *subscriptions)
	}
	return nil
}

func getBaseSubscription() MembershipData {
	return MembershipData{
		APIKey: config.Auth.APIKEY,
		Model: models.SubscriptionModel{
			Name:       "Premium",
			Details:    "A subscription detail",
			MonthlyFee: 12.0,
			HourlyRate: 14.0,
		},
	}
}
func customizeSubscription(customize func(MembershipData) MembershipData) MembershipData {
	subscription := getBaseSubscription()
	return customize(subscription)
}

func getSubscriptionData() []RegisterSubscriptionTest {
	return []RegisterSubscriptionTest{
		{
			Name:         "Missing details should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"name": "Just a Subscription"},
			Assert:       false,
			Input: GenerateInputJSON(
				customizeSubscription(func(subscription MembershipData) MembershipData {
					subscription.Model.Details = ""
					return subscription
				})),
		},
		{
			Name:         "Missing model name should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"name": ""},
			Assert:       false,
			Input: GenerateInputJSON(
				customizeSubscription(func(subscription MembershipData) MembershipData {
					subscription.Model.Name = ""
					return subscription
				})),
		},
		{
			Name:         "Negative monthly fee should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"name": "Premium"},
			Assert:       false,
			Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData {
				sub.Model.MonthlyFee = -10.0
				return sub
			})),
		},
		{
			Name:         "Negative hourly rate should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"name": "Premium"},
			Assert:       false,
			Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData {
				sub.Model.HourlyRate = -1.0
				return sub
			})),
		},
		{
			Name:         "correct entry should pass",
			WantResponse: http.StatusCreated,
			WantDBData:   map[string]interface{}{"name": "Premium"},
			Assert:       true,
			Input: GenerateInputJSON(
				customizeSubscription(func(subscription MembershipData) MembershipData {
					subscription.Model.Conditions = "Some Condition"
					subscription.Model.IncludedPerYear = 0
					subscription.Model.IncludedPerMonth = 1
					return subscription
				})),
		},
		{
			Name:         "Duplicate subscription name should fail",
			WantResponse: http.StatusConflict,
			WantDBData:   map[string]interface{}{"name": "Premium"},
			Assert:       true, // The original subscription should still exist
			Input:        GenerateInputJSON(getBaseSubscription()),
		},
	}
}
