package handler

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"eslogad-be/internal/contract"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// MockMasterService is a mock implementation of MasterService
type MockMasterService struct {
	mock.Mock
}

func (m *MockMasterService) CreateLabel(ctx context.Context, req *contract.CreateLabelRequest) (*contract.LabelResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*contract.LabelResponse), args.Error(1)
}

func (m *MockMasterService) UpdateLabel(ctx context.Context, id uuid.UUID, req *contract.UpdateLabelRequest) (*contract.LabelResponse, error) {
	args := m.Called(ctx, id, req)
	return args.Get(0).(*contract.LabelResponse), args.Error(1)
}

func (m *MockMasterService) DeleteLabel(ctx context.Context, id uuid.UUID) error {
	args := m.Called(ctx, id)
	return args.Error(0)
}

func (m *MockMasterService) ListLabels(ctx context.Context) (*contract.ListLabelsResponse, error) {
	args := m.Called(ctx)
	return args.Get(0).(*contract.ListLabelsResponse), args.Error(1)
}

func (m *MockMasterService) CreatePriority(ctx context.Context, req *contract.CreatePriorityRequest) (*contract.PriorityResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*contract.PriorityResponse), args.Error(1)
}

func (m *MockMasterService) UpdatePriority(ctx context.Context, id uuid.UUID, req *contract.UpdatePriorityRequest) (*contract.PriorityResponse, error) {
	args := m.Called(ctx, id, req)
	return args.Get(0).(*contract.PriorityResponse), args.Error(1)
}

func (m *MockMasterService) DeletePriority(ctx context.Context, id uuid.UUID) error {
	args := m.Called(ctx, id)
	return args.Error(0)
}

func (m *MockMasterService) ListPriorities(ctx context.Context) (*contract.ListPrioritiesResponse, error) {
	args := m.Called(ctx)
	return args.Get(0).(*contract.ListPrioritiesResponse), args.Error(1)
}

func (m *MockMasterService) CreateInstitution(ctx context.Context, req *contract.CreateInstitutionRequest) (*contract.InstitutionResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*contract.InstitutionResponse), args.Error(1)
}

func (m *MockMasterService) UpdateInstitution(ctx context.Context, id uuid.UUID, req *contract.UpdateInstitutionRequest) (*contract.InstitutionResponse, error) {
	args := m.Called(ctx, id, req)
	return args.Get(0).(*contract.InstitutionResponse), args.Error(1)
}

func (m *MockMasterService) DeleteInstitution(ctx context.Context, id uuid.UUID) error {
	args := m.Called(ctx, id)
	return args.Error(0)
}

func (m *MockMasterService) ListInstitutions(ctx context.Context, req *contract.ListInstitutionsRequest) (*contract.ListInstitutionsResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*contract.ListInstitutionsResponse), args.Error(1)
}

func (m *MockMasterService) CreateDispositionAction(ctx context.Context, req *contract.CreateDispositionActionRequest) (*contract.DispositionActionResponse, error) {
	args := m.Called(ctx, req)
	return args.Get(0).(*contract.DispositionActionResponse), args.Error(1)
}

func (m *MockMasterService) UpdateDispositionAction(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionActionRequest) (*contract.DispositionActionResponse, error) {
	args := m.Called(ctx, id, req)
	return args.Get(0).(*contract.DispositionActionResponse), args.Error(1)
}

func (m *MockMasterService) DeleteDispositionAction(ctx context.Context, id uuid.UUID) error {
	args := m.Called(ctx, id)
	return args.Error(0)
}

func (m *MockMasterService) ListDispositionActions(ctx context.Context) (*contract.ListDispositionActionsResponse, error) {
	args := m.Called(ctx)
	return args.Get(0).(*contract.ListDispositionActionsResponse), args.Error(1)
}

func TestMasterHandler_ListInstitutions_WithSearch(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	mockService := new(MockMasterService)
	handler := NewMasterHandler(mockService)

	// Test data
	searchTerm := "university"
	expectedResponse := &contract.ListInstitutionsResponse{
		Institutions: []contract.InstitutionResponse{
			{
				ID:   "123",
				Name: "Test University",
				Type: "university",
			},
		},
	}

	// Setup mock expectations
	mockService.On("ListInstitutions", mock.Anything, &contract.ListInstitutionsRequest{
		Search: &searchTerm,
	}).Return(expectedResponse, nil)

	// Create request
	req, _ := http.NewRequest("GET", "/institutions?search="+searchTerm, nil)
	w := httptest.NewRecorder()

	// Create gin context
	c, _ := gin.CreateTestContext(w)
	c.Request = req

	// Execute
	handler.ListInstitutions(c)

	// Assertions
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)

	// Verify mock was called correctly
	mockService.AssertExpectations(t)
}

func TestMasterHandler_ListInstitutions_WithoutSearch(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	mockService := new(MockMasterService)
	handler := NewMasterHandler(mockService)

	// Test data
	expectedResponse := &contract.ListInstitutionsResponse{
		Institutions: []contract.InstitutionResponse{
			{
				ID:   "123",
				Name: "Test Institution",
				Type: "company",
			},
		},
	}

	// Setup mock expectations
	mockService.On("ListInstitutions", mock.Anything, &contract.ListInstitutionsRequest{
		Search: nil,
	}).Return(expectedResponse, nil)

	// Create request
	req, _ := http.NewRequest("GET", "/institutions", nil)
	w := httptest.NewRecorder()

	// Create gin context
	c, _ := gin.CreateTestContext(w)
	c.Request = req

	// Execute
	handler.ListInstitutions(c)

	// Assertions
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)

	// Verify mock was called correctly
	mockService.AssertExpectations(t)
}
