package middleware

import (
	"context"
	"errors"
	"testing"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

type MockShopStore struct {
	mock.Mock
}

func (m *MockShopStore) GetByID(ctx context.Context, id uint) (*model.Shop, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*model.Shop), args.Error(1)
}

func (m *MockShopStore) GetSubordinateShopIDs(ctx context.Context, shopID uint) ([]uint, error) {
	args := m.Called(ctx, shopID)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]uint), args.Error(1)
}

type MockEnterpriseStore struct {
	mock.Mock
}

func (m *MockEnterpriseStore) GetByID(ctx context.Context, id uint) (*model.Enterprise, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*model.Enterprise), args.Error(1)
}

func TestCanManageShop_SuperAdmin(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeSuperAdmin)

	mockShopStore := new(MockShopStore)

	err := CanManageShop(ctx, 100, mockShopStore)
	assert.NoError(t, err)

	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageShop_Platform(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypePlatform)

	mockShopStore := new(MockShopStore)

	err := CanManageShop(ctx, 100, mockShopStore)
	assert.NoError(t, err)

	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageShop_AgentManageOwnShop(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return([]uint{100, 101, 102}, nil)

	err := CanManageShop(ctx, 100, mockShopStore)
	assert.NoError(t, err)

	mockShopStore.AssertExpectations(t)
}

func TestCanManageShop_AgentManageSubordinateShop(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return([]uint{100, 101, 102}, nil)

	err := CanManageShop(ctx, 101, mockShopStore)
	assert.NoError(t, err)

	mockShopStore.AssertExpectations(t)
}

func TestCanManageShop_AgentCannotManageOtherShop(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return([]uint{100, 101, 102}, nil)

	err := CanManageShop(ctx, 200, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理该店铺的账号")

	mockShopStore.AssertExpectations(t)
}

func TestCanManageShop_AgentNoShopID(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)

	mockShopStore := new(MockShopStore)

	err := CanManageShop(ctx, 100, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理店铺账号")

	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageShop_EnterpriseUser(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeEnterprise)

	mockShopStore := new(MockShopStore)

	err := CanManageShop(ctx, 100, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理店铺账号")

	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageShop_GetSubordinateShopIDsError(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return(nil, errors.New("database error"))

	err := CanManageShop(ctx, 100, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "查询下级店铺失败")

	mockShopStore.AssertExpectations(t)
}

func TestCanManageEnterprise_SuperAdmin(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeSuperAdmin)

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockShopStore := new(MockShopStore)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.NoError(t, err)

	mockEnterpriseStore.AssertNotCalled(t, "GetByID")
	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageEnterprise_Platform(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypePlatform)

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockShopStore := new(MockShopStore)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.NoError(t, err)

	mockEnterpriseStore.AssertNotCalled(t, "GetByID")
	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageEnterprise_AgentManageOwnShopEnterprise(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	ownerShopID := uint(100)
	enterprise := &model.Enterprise{
		OwnerShopID: &ownerShopID,
	}

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(enterprise, nil)

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return([]uint{100, 101, 102}, nil)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.NoError(t, err)

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertExpectations(t)
}

func TestCanManageEnterprise_AgentManageSubordinateShopEnterprise(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	ownerShopID := uint(101)
	enterprise := &model.Enterprise{
		OwnerShopID: &ownerShopID,
	}

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(enterprise, nil)

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return([]uint{100, 101, 102}, nil)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.NoError(t, err)

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertExpectations(t)
}

func TestCanManageEnterprise_AgentCannotManageOtherShopEnterprise(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	ownerShopID := uint(200)
	enterprise := &model.Enterprise{
		OwnerShopID: &ownerShopID,
	}

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(enterprise, nil)

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return([]uint{100, 101, 102}, nil)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理该企业的账号")

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertExpectations(t)
}

func TestCanManageEnterprise_AgentCannotManagePlatformEnterprise(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	enterprise := &model.Enterprise{
		OwnerShopID: nil,
	}

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(enterprise, nil)

	mockShopStore := new(MockShopStore)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理平台级企业账号")

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageEnterprise_EnterpriseUser(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeEnterprise)

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockShopStore := new(MockShopStore)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理企业账号")

	mockEnterpriseStore.AssertNotCalled(t, "GetByID")
	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageEnterprise_GetEnterpriseError(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(nil, errors.New("database error"))

	mockShopStore := new(MockShopStore)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限操作该资源或资源不存在")

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageEnterprise_AgentNoShopID(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)

	ownerShopID := uint(100)
	enterprise := &model.Enterprise{
		OwnerShopID: &ownerShopID,
	}

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(enterprise, nil)

	mockShopStore := new(MockShopStore)

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "无权限管理企业账号")

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertNotCalled(t, "GetSubordinateShopIDs")
}

func TestCanManageEnterprise_GetSubordinateShopIDsError(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypeAgent)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, uint(100))

	ownerShopID := uint(100)
	enterprise := &model.Enterprise{
		OwnerShopID: &ownerShopID,
	}

	mockEnterpriseStore := new(MockEnterpriseStore)
	mockEnterpriseStore.On("GetByID", ctx, uint(50)).Return(enterprise, nil)

	mockShopStore := new(MockShopStore)
	mockShopStore.On("GetSubordinateShopIDs", ctx, uint(100)).Return(nil, errors.New("database error"))

	err := CanManageEnterprise(ctx, 50, mockEnterpriseStore, mockShopStore)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "查询下级店铺失败")

	mockEnterpriseStore.AssertExpectations(t)
	mockShopStore.AssertExpectations(t)
}

func TestPermissionHelperTestCoverage(t *testing.T) {
	mockShopStore := new(MockShopStore)
	mockEnterpriseStore := new(MockEnterpriseStore)

	assert.Implements(t, (*ShopStoreInterface)(nil), mockShopStore)
	assert.Implements(t, (*EnterpriseStoreInterface)(nil), mockEnterpriseStore)
}
