package handler

import (
	"baron-sso-backend/internal/domain"
	"baron-sso-backend/internal/service"
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gofiber/fiber/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// --- Mocks ---

type MockUserGroupService struct {
	mock.Mock
}

func (m *MockUserGroupService) Create(ctx context.Context, tenantID string, parentID *string, name, description, unitType string) (*domain.UserGroup, error) {
	args := m.Called(ctx, tenantID, parentID, name, description, unitType)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*domain.UserGroup), args.Error(1)
}

func (m *MockUserGroupService) Update(ctx context.Context, tenantID, groupID string, name, description, unitType string, parentID *string) (*domain.UserGroup, error) {
	args := m.Called(ctx, tenantID, groupID, name, description, unitType, parentID)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*domain.UserGroup), args.Error(1)
}

func (m *MockUserGroupService) Delete(ctx context.Context, tenantID, groupID string) error {
	return m.Called(ctx, tenantID, groupID).Error(0)
}

func (m *MockUserGroupService) Get(ctx context.Context, id string) (*domain.UserGroup, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*domain.UserGroup), args.Error(1)
}

func (m *MockUserGroupService) List(ctx context.Context, tenantID string) ([]domain.UserGroup, error) {
	args := m.Called(ctx, tenantID)
	return args.Get(0).([]domain.UserGroup), args.Error(1)
}

func (m *MockUserGroupService) SetWorksmobileSyncer(syncer service.WorksmobileSyncer) {}

func (m *MockUserGroupService) AddMember(ctx context.Context, groupID, userID string) error {
	return m.Called(ctx, groupID, userID).Error(0)
}

func (m *MockUserGroupService) RemoveMember(ctx context.Context, groupID, userID string) error {
	return m.Called(ctx, groupID, userID).Error(0)
}

func (m *MockUserGroupService) ListRoles(ctx context.Context, groupID string) ([]domain.GroupRole, error) {
	args := m.Called(ctx, groupID)
	return args.Get(0).([]domain.GroupRole), args.Error(1)
}

func (m *MockUserGroupService) AssignRoleToTenant(ctx context.Context, groupID, tenantID, relation string) error {
	return m.Called(ctx, groupID, tenantID, relation).Error(0)
}

func (m *MockUserGroupService) RemoveRoleFromTenant(ctx context.Context, groupID, tenantID, relation string) error {
	return m.Called(ctx, groupID, tenantID, relation).Error(0)
}

// --- Tests ---

func TestUserGroupHandler_List(t *testing.T) {
	mockSvc := new(MockUserGroupService)
	h := NewUserGroupHandler(mockSvc)
	app := fiber.New()
	app.Get("/tenants/:tenantId/user-groups", h.List)

	tenantID := "t1"
	groups := []domain.UserGroup{{ID: "g1", Name: "Group 1"}}
	mockSvc.On("List", mock.Anything, tenantID).Return(groups, nil)

	req := httptest.NewRequest("GET", "/tenants/t1/user-groups", nil)
	resp, _ := app.Test(req)

	assert.Equal(t, http.StatusOK, resp.StatusCode)
	var result []domain.UserGroup
	json.NewDecoder(resp.Body).Decode(&result)
	assert.Len(t, result, 1)
	assert.Equal(t, "Group 1", result[0].Name)
}

func TestUserGroupHandler_Create(t *testing.T) {
	mockSvc := new(MockUserGroupService)
	h := NewUserGroupHandler(mockSvc)
	app := fiber.New()
	app.Post("/tenants/:tenantId/user-groups", h.Create)

	body, _ := json.Marshal(map[string]string{"name": "New Group"})
	mockSvc.On("Create", mock.Anything, "t1", mock.Anything, "New Group", mock.Anything, mock.Anything).Return(&domain.UserGroup{ID: "g1", Name: "New Group"}, nil)

	req := httptest.NewRequest("POST", "/tenants/t1/user-groups", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	resp, _ := app.Test(req)

	assert.Equal(t, http.StatusCreated, resp.StatusCode)
}

func TestUserGroupHandler_AddMember(t *testing.T) {
	mockSvc := new(MockUserGroupService)
	h := NewUserGroupHandler(mockSvc)
	app := fiber.New()
	app.Post("/user-groups/:id/members", h.AddMember)

	groupID := "g1"
	userID := "u1"
	body, _ := json.Marshal(map[string]string{"userId": userID})

	mockSvc.On("AddMember", mock.Anything, groupID, userID).Return(nil)

	req := httptest.NewRequest("POST", "/user-groups/g1/members", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	resp, _ := app.Test(req)

	assert.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestUserGroupHandler_AssignRole(t *testing.T) {
	mockSvc := new(MockUserGroupService)
	h := NewUserGroupHandler(mockSvc)
	app := fiber.New()
	app.Post("/user-groups/:id/roles", h.AssignRole)

	groupID := "g1"
	targetTenantID := "t2"
	relation := "manage"
	body, _ := json.Marshal(map[string]string{"tenantId": targetTenantID, "relation": relation})

	mockSvc.On("AssignRoleToTenant", mock.Anything, groupID, targetTenantID, relation).Return(nil)

	req := httptest.NewRequest("POST", "/user-groups/g1/roles", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	resp, _ := app.Test(req)

	assert.Equal(t, http.StatusOK, resp.StatusCode)
}
