package integration

import (
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/response"
	"github.com/break/junhong_cmp_fiber/tests/testutils/integ"
)

func TestRoleAPI_Create(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	t.Run("成功创建角色", func(t *testing.T) {
		roleName := fmt.Sprintf("test_role_%d", time.Now().UnixNano())
		reqBody := dto.CreateRoleRequest{
			RoleName: roleName,
			RoleDesc: "这是一个测试角色",
			RoleType: constants.RoleTypePlatform,
		}

		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/roles", jsonBody)
		require.NoError(t, err)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		t.Logf("响应状态码: %d, 业务码: %d, 消息: %s", resp.StatusCode, result.Code, result.Message)

		assert.Equal(t, fiber.StatusOK, resp.StatusCode)
		assert.Equal(t, 0, result.Code, "业务码应为0，实际消息: %s", result.Message)

		var count int64
		env.RawDB().Model(&model.Role{}).Where("role_name = ?", roleName).Count(&count)
		t.Logf("查询角色 '%s' 数量: %d", roleName, count)
		assert.Equal(t, int64(1), count)
	})

	t.Run("缺少必填字段返回错误", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"role_desc": "缺少名称",
		}
		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/roles", jsonBody)
		require.NoError(t, err)
		var result response.Response
		json.NewDecoder(resp.Body).Decode(&result)
		assert.NotEqual(t, 0, result.Code)
	})
}

func TestRoleAPI_Get(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	testRole := env.CreateTestRole("获取测试角色", constants.RoleTypePlatform)

	t.Run("成功获取角色详情", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/roles/%d", testRole.ID), nil)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)
	})

	t.Run("角色不存在时返回错误", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/roles/99999", nil)
		require.NoError(t, err)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, errors.CodeRoleNotFound, result.Code)
	})
}

func TestRoleAPI_Update(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	testRole := env.CreateTestRole("更新测试角色", constants.RoleTypePlatform)

	t.Run("成功更新角色", func(t *testing.T) {
		newName := "更新后角色"
		reqBody := dto.UpdateRoleRequest{
			RoleName: &newName,
		}

		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("PUT", fmt.Sprintf("/api/admin/roles/%d", testRole.ID), jsonBody)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var updated model.Role
		env.RawDB().First(&updated, testRole.ID)
		assert.Equal(t, newName, updated.RoleName)
	})
}

func TestRoleAPI_Delete(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	t.Run("成功软删除角色", func(t *testing.T) {
		testRole := env.CreateTestRole("删除测试角色", constants.RoleTypePlatform)

		resp, err := env.AsSuperAdmin().Request("DELETE", fmt.Sprintf("/api/admin/roles/%d", testRole.ID), nil)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var deleted model.Role
		err = env.RawDB().Unscoped().First(&deleted, testRole.ID).Error
		require.NoError(t, err)
		assert.NotNil(t, deleted.DeletedAt)
	})
}

func TestRoleAPI_List(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	for i := 1; i <= 5; i++ {
		env.CreateTestRole(fmt.Sprintf("test_role_%d_%d", time.Now().UnixNano(), i), constants.RoleTypePlatform)
	}

	t.Run("成功获取角色列表", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/roles?page=1&page_size=10", nil)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)
	})
}

func TestRoleAPI_AssignPermissions(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	testRole := env.CreateTestRole("权限分配测试角色", constants.RoleTypePlatform)
	testPerm := env.CreateTestPermission("测试权限", "test:permission", constants.PermissionTypeMenu)

	t.Run("成功分配权限", func(t *testing.T) {
		reqBody := dto.AssignPermissionsRequest{
			PermIDs: []uint{testPerm.ID},
		}

		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("POST", fmt.Sprintf("/api/admin/roles/%d/permissions", testRole.ID), jsonBody)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var count int64
		env.RawDB().Model(&model.RolePermission{}).Where("role_id = ? AND perm_id = ?", testRole.ID, testPerm.ID).Count(&count)
		assert.Equal(t, int64(1), count)
	})
}

func TestRoleAPI_GetPermissions(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	testRole := env.CreateTestRole("获取权限测试角色", constants.RoleTypePlatform)
	testPerm := env.CreateTestPermission("获取权限测试", "get:permission:test", constants.PermissionTypeMenu)

	rolePerm := &model.RolePermission{
		RoleID: testRole.ID,
		PermID: testPerm.ID,
		Status: constants.StatusEnabled,
	}
	env.TX.Create(rolePerm)

	t.Run("成功获取角色权限", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/roles/%d/permissions", testRole.ID), nil)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)
	})
}

func TestRoleAPI_RemovePermission(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	testRole := env.CreateTestRole("移除权限测试角色", constants.RoleTypePlatform)
	testPerm := env.CreateTestPermission("移除权限测试", "remove:permission:test", constants.PermissionTypeMenu)

	rolePerm := &model.RolePermission{
		RoleID: testRole.ID,
		PermID: testPerm.ID,
		Status: constants.StatusEnabled,
	}
	env.TX.Create(rolePerm)

	t.Run("成功移除权限", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("DELETE", fmt.Sprintf("/api/admin/roles/%d/permissions/%d", testRole.ID, testPerm.ID), nil)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var rp model.RolePermission
		err = env.RawDB().Unscoped().Where("role_id = ? AND perm_id = ?", testRole.ID, testPerm.ID).First(&rp).Error
		require.NoError(t, err)
		assert.NotNil(t, rp.DeletedAt)
	})
}

func TestRoleAPI_UpdateStatus(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	testRole := env.CreateTestRole("状态切换测试角色", constants.RoleTypePlatform)

	t.Run("成功禁用角色", func(t *testing.T) {
		reqBody := dto.UpdateRoleStatusRequest{
			Status: intPtr(constants.StatusDisabled),
		}

		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("PUT", fmt.Sprintf("/api/admin/roles/%d/status", testRole.ID), jsonBody)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)

		var updated model.Role
		env.RawDB().First(&updated, testRole.ID)
		assert.Equal(t, constants.StatusDisabled, updated.Status)
	})

	t.Run("成功启用角色", func(t *testing.T) {
		reqBody := dto.UpdateRoleStatusRequest{
			Status: intPtr(constants.StatusEnabled),
		}

		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("PUT", fmt.Sprintf("/api/admin/roles/%d/status", testRole.ID), jsonBody)
		require.NoError(t, err)
		assert.Equal(t, fiber.StatusOK, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)

		var updated model.Role
		env.RawDB().First(&updated, testRole.ID)
		assert.Equal(t, constants.StatusEnabled, updated.Status)
	})

	t.Run("角色不存在返回错误", func(t *testing.T) {
		reqBody := dto.UpdateRoleStatusRequest{
			Status: intPtr(constants.StatusEnabled),
		}

		jsonBody, _ := json.Marshal(reqBody)
		resp, err := env.AsSuperAdmin().Request("PUT", "/api/admin/roles/99999/status", jsonBody)
		require.NoError(t, err)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, errors.CodeRoleNotFound, result.Code)
	})
}

// intPtr 返回 int 的指针
func intPtr(v int) *int {
	return &v
}
