package postgres

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/tests/testutils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func uniqueDeviceAuthTestPrefix() string {
	return fmt.Sprintf("EDA%d", time.Now().UnixNano()%1000000000)
}

func TestEnterpriseDeviceAuthorizationStore_Create(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	device := &model.Device{
		DeviceNo:   prefix + "_001",
		DeviceName: "测试设备1",
		Status:     2,
	}
	require.NoError(t, tx.Create(device).Error)

	t.Run("成功创建授权记录", func(t *testing.T) {
		auth := &model.EnterpriseDeviceAuthorization{
			EnterpriseID:   enterprise.ID,
			DeviceID:       device.ID,
			AuthorizedBy:   1,
			AuthorizedAt:   time.Now(),
			AuthorizerType: 2,
			Remark:         "测试授权",
		}

		err := store.Create(ctx, auth)
		require.NoError(t, err)
		assert.NotZero(t, auth.ID)
		assert.Equal(t, enterprise.ID, auth.EnterpriseID)
		assert.Equal(t, device.ID, auth.DeviceID)
	})
}

func TestEnterpriseDeviceAuthorizationStore_BatchCreate(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	devices := []*model.Device{
		{DeviceNo: prefix + "_001", DeviceName: "测试设备1", Status: 2},
		{DeviceNo: prefix + "_002", DeviceName: "测试设备2", Status: 2},
		{DeviceNo: prefix + "_003", DeviceName: "测试设备3", Status: 2},
	}
	for _, d := range devices {
		require.NoError(t, tx.Create(d).Error)
	}

	t.Run("成功批量创建授权记录", func(t *testing.T) {
		now := time.Now()
		auths := []*model.EnterpriseDeviceAuthorization{
			{EnterpriseID: enterprise.ID, DeviceID: devices[0].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
			{EnterpriseID: enterprise.ID, DeviceID: devices[1].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
			{EnterpriseID: enterprise.ID, DeviceID: devices[2].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
		}

		err := store.BatchCreate(ctx, auths)
		require.NoError(t, err)

		for _, auth := range auths {
			assert.NotZero(t, auth.ID)
		}
	})

	t.Run("空列表不报错", func(t *testing.T) {
		err := store.BatchCreate(ctx, []*model.EnterpriseDeviceAuthorization{})
		require.NoError(t, err)
	})
}

func TestEnterpriseDeviceAuthorizationStore_GetByID(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	device := &model.Device{
		DeviceNo:   prefix + "_001",
		DeviceName: "测试设备1",
		Status:     2,
	}
	require.NoError(t, tx.Create(device).Error)

	auth := &model.EnterpriseDeviceAuthorization{
		EnterpriseID:   enterprise.ID,
		DeviceID:       device.ID,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: 2,
		Remark:         "测试备注",
	}
	require.NoError(t, store.Create(ctx, auth))

	t.Run("成功获取授权记录", func(t *testing.T) {
		result, err := store.GetByID(ctx, auth.ID)
		require.NoError(t, err)
		assert.Equal(t, auth.ID, result.ID)
		assert.Equal(t, enterprise.ID, result.EnterpriseID)
		assert.Equal(t, device.ID, result.DeviceID)
		assert.Equal(t, "测试备注", result.Remark)
	})

	t.Run("记录不存在返回错误", func(t *testing.T) {
		_, err := store.GetByID(ctx, 99999)
		require.Error(t, err)
	})
}

func TestEnterpriseDeviceAuthorizationStore_GetByDeviceID(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	device := &model.Device{
		DeviceNo:   prefix + "_001",
		DeviceName: "测试设备1",
		Status:     2,
	}
	require.NoError(t, tx.Create(device).Error)

	auth := &model.EnterpriseDeviceAuthorization{
		EnterpriseID:   enterprise.ID,
		DeviceID:       device.ID,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: 2,
	}
	require.NoError(t, store.Create(ctx, auth))

	t.Run("成功通过设备ID获取授权记录", func(t *testing.T) {
		result, err := store.GetByDeviceID(ctx, device.ID)
		require.NoError(t, err)
		assert.Equal(t, auth.ID, result.ID)
		assert.Equal(t, enterprise.ID, result.EnterpriseID)
	})

	t.Run("设备未授权返回错误", func(t *testing.T) {
		_, err := store.GetByDeviceID(ctx, 99999)
		require.Error(t, err)
	})

	t.Run("已撤销的授权不返回", func(t *testing.T) {
		device2 := &model.Device{
			DeviceNo:   prefix + "_002",
			DeviceName: "测试设备2",
			Status:     2,
		}
		require.NoError(t, tx.Create(device2).Error)

		now := time.Now()
		revokedAuth := &model.EnterpriseDeviceAuthorization{
			EnterpriseID:   enterprise.ID,
			DeviceID:       device2.ID,
			AuthorizedBy:   1,
			AuthorizedAt:   now,
			AuthorizerType: 2,
			RevokedBy:      ptrUint(1),
			RevokedAt:      &now,
		}
		require.NoError(t, store.Create(ctx, revokedAuth))

		_, err := store.GetByDeviceID(ctx, device2.ID)
		require.Error(t, err)
	})
}

func TestEnterpriseDeviceAuthorizationStore_GetByEnterpriseID(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	devices := []*model.Device{
		{DeviceNo: prefix + "_001", DeviceName: "测试设备1", Status: 2},
		{DeviceNo: prefix + "_002", DeviceName: "测试设备2", Status: 2},
	}
	for _, d := range devices {
		require.NoError(t, tx.Create(d).Error)
	}

	now := time.Now()
	auths := []*model.EnterpriseDeviceAuthorization{
		{EnterpriseID: enterprise.ID, DeviceID: devices[0].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
		{EnterpriseID: enterprise.ID, DeviceID: devices[1].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2, RevokedBy: ptrUint(1), RevokedAt: &now},
	}
	for _, auth := range auths {
		require.NoError(t, store.Create(ctx, auth))
	}

	t.Run("获取未撤销的授权记录", func(t *testing.T) {
		result, err := store.GetByEnterpriseID(ctx, enterprise.ID, false)
		require.NoError(t, err)
		assert.Len(t, result, 1)
		assert.Equal(t, devices[0].ID, result[0].DeviceID)
	})

	t.Run("获取所有授权记录包括已撤销", func(t *testing.T) {
		result, err := store.GetByEnterpriseID(ctx, enterprise.ID, true)
		require.NoError(t, err)
		assert.Len(t, result, 2)
	})
}

func TestEnterpriseDeviceAuthorizationStore_ListByEnterprise(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	devices := make([]*model.Device, 5)
	for i := 0; i < 5; i++ {
		devices[i] = &model.Device{
			DeviceNo:   fmt.Sprintf("%s_%03d", prefix, i+1),
			DeviceName: fmt.Sprintf("测试设备%d", i+1),
			Status:     2,
		}
		require.NoError(t, tx.Create(devices[i]).Error)
	}

	now := time.Now()
	for i, d := range devices {
		auth := &model.EnterpriseDeviceAuthorization{
			EnterpriseID:   enterprise.ID,
			DeviceID:       d.ID,
			AuthorizedBy:   uint(i + 1),
			AuthorizedAt:   now.Add(time.Duration(i) * time.Minute),
			AuthorizerType: 2,
		}
		require.NoError(t, store.Create(ctx, auth))
	}

	t.Run("分页查询", func(t *testing.T) {
		opts := DeviceAuthListOptions{
			EnterpriseID: &enterprise.ID,
			Page:         1,
			PageSize:     2,
		}

		result, total, err := store.ListByEnterprise(ctx, opts)
		require.NoError(t, err)
		assert.Equal(t, int64(5), total)
		assert.Len(t, result, 2)
	})

	t.Run("按授权人过滤", func(t *testing.T) {
		authorizerID := uint(1)
		opts := DeviceAuthListOptions{
			EnterpriseID: &enterprise.ID,
			AuthorizerID: &authorizerID,
			Page:         1,
			PageSize:     10,
		}

		result, total, err := store.ListByEnterprise(ctx, opts)
		require.NoError(t, err)
		assert.Equal(t, int64(1), total)
		assert.Len(t, result, 1)
	})

	t.Run("按设备ID过滤", func(t *testing.T) {
		opts := DeviceAuthListOptions{
			EnterpriseID: &enterprise.ID,
			DeviceIDs:    []uint{devices[0].ID, devices[1].ID},
			Page:         1,
			PageSize:     10,
		}

		result, total, err := store.ListByEnterprise(ctx, opts)
		require.NoError(t, err)
		assert.Equal(t, int64(2), total)
		assert.Len(t, result, 2)
	})
}

func TestEnterpriseDeviceAuthorizationStore_RevokeByIDs(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	devices := []*model.Device{
		{DeviceNo: prefix + "_001", DeviceName: "测试设备1", Status: 2},
		{DeviceNo: prefix + "_002", DeviceName: "测试设备2", Status: 2},
	}
	for _, d := range devices {
		require.NoError(t, tx.Create(d).Error)
	}

	now := time.Now()
	auths := []*model.EnterpriseDeviceAuthorization{
		{EnterpriseID: enterprise.ID, DeviceID: devices[0].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
		{EnterpriseID: enterprise.ID, DeviceID: devices[1].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
	}
	for _, auth := range auths {
		require.NoError(t, store.Create(ctx, auth))
	}

	t.Run("成功撤销授权", func(t *testing.T) {
		revokerID := uint(2)
		err := store.RevokeByIDs(ctx, []uint{auths[0].ID}, revokerID)
		require.NoError(t, err)

		result, err := store.GetByID(ctx, auths[0].ID)
		require.NoError(t, err)
		assert.NotNil(t, result.RevokedAt)
		assert.NotNil(t, result.RevokedBy)
		assert.Equal(t, revokerID, *result.RevokedBy)
	})

	t.Run("已撤销的记录不再被重复撤销", func(t *testing.T) {
		err := store.RevokeByIDs(ctx, []uint{auths[0].ID}, uint(3))
		require.NoError(t, err)

		result, err := store.GetByID(ctx, auths[0].ID)
		require.NoError(t, err)
		assert.Equal(t, uint(2), *result.RevokedBy)
	})
}

func TestEnterpriseDeviceAuthorizationStore_GetActiveAuthsByDeviceIDs(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	devices := []*model.Device{
		{DeviceNo: prefix + "_001", DeviceName: "测试设备1", Status: 2},
		{DeviceNo: prefix + "_002", DeviceName: "测试设备2", Status: 2},
		{DeviceNo: prefix + "_003", DeviceName: "测试设备3", Status: 2},
	}
	for _, d := range devices {
		require.NoError(t, tx.Create(d).Error)
	}

	now := time.Now()
	auths := []*model.EnterpriseDeviceAuthorization{
		{EnterpriseID: enterprise.ID, DeviceID: devices[0].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
		{EnterpriseID: enterprise.ID, DeviceID: devices[1].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2, RevokedBy: ptrUint(1), RevokedAt: &now},
	}
	for _, auth := range auths {
		require.NoError(t, store.Create(ctx, auth))
	}

	t.Run("获取有效授权的设备ID映射", func(t *testing.T) {
		deviceIDs := []uint{devices[0].ID, devices[1].ID, devices[2].ID}
		result, err := store.GetActiveAuthsByDeviceIDs(ctx, enterprise.ID, deviceIDs)
		require.NoError(t, err)

		assert.True(t, result[devices[0].ID])
		assert.False(t, result[devices[1].ID])
		assert.False(t, result[devices[2].ID])
	})

	t.Run("空设备ID列表返回空映射", func(t *testing.T) {
		result, err := store.GetActiveAuthsByDeviceIDs(ctx, enterprise.ID, []uint{})
		require.NoError(t, err)
		assert.Empty(t, result)
	})
}

func TestEnterpriseDeviceAuthorizationStore_ListDeviceIDsByEnterprise(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	store := NewEnterpriseDeviceAuthorizationStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueDeviceAuthTestPrefix()

	enterprise := &model.Enterprise{
		EnterpriseName: prefix + "_测试企业",
		EnterpriseCode: prefix,
		Status:         1,
	}
	require.NoError(t, tx.Create(enterprise).Error)

	devices := []*model.Device{
		{DeviceNo: prefix + "_001", DeviceName: "测试设备1", Status: 2},
		{DeviceNo: prefix + "_002", DeviceName: "测试设备2", Status: 2},
	}
	for _, d := range devices {
		require.NoError(t, tx.Create(d).Error)
	}

	now := time.Now()
	auths := []*model.EnterpriseDeviceAuthorization{
		{EnterpriseID: enterprise.ID, DeviceID: devices[0].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
		{EnterpriseID: enterprise.ID, DeviceID: devices[1].ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: 2},
	}
	for _, auth := range auths {
		require.NoError(t, store.Create(ctx, auth))
	}

	t.Run("获取企业授权设备ID列表", func(t *testing.T) {
		result, err := store.ListDeviceIDsByEnterprise(ctx, enterprise.ID)
		require.NoError(t, err)
		assert.Len(t, result, 2)
		assert.Contains(t, result, devices[0].ID)
		assert.Contains(t, result, devices[1].ID)
	})

	t.Run("无授权记录返回空列表", func(t *testing.T) {
		result, err := store.ListDeviceIDsByEnterprise(ctx, 99999)
		require.NoError(t, err)
		assert.Empty(t, result)
	})
}

func ptrUint(v uint) *uint {
	return &v
}
