package enterprise_device

import (
	"context"
	stderrors "errors"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/jackc/pgx/v5/pgconn"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type Service struct {
	db                        *gorm.DB
	enterpriseStore           *postgres.EnterpriseStore
	deviceStore               *postgres.DeviceStore
	deviceSimBindingStore     *postgres.DeviceSimBindingStore
	enterpriseDeviceAuthStore *postgres.EnterpriseDeviceAuthorizationStore
	enterpriseCardAuthStore   *postgres.EnterpriseCardAuthorizationStore
	logger                    *zap.Logger
}

func New(
	db *gorm.DB,
	enterpriseStore *postgres.EnterpriseStore,
	deviceStore *postgres.DeviceStore,
	deviceSimBindingStore *postgres.DeviceSimBindingStore,
	enterpriseDeviceAuthStore *postgres.EnterpriseDeviceAuthorizationStore,
	enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore,
	logger *zap.Logger,
) *Service {
	return &Service{
		db:                        db,
		enterpriseStore:           enterpriseStore,
		deviceStore:               deviceStore,
		deviceSimBindingStore:     deviceSimBindingStore,
		enterpriseDeviceAuthStore: enterpriseDeviceAuthStore,
		enterpriseCardAuthStore:   enterpriseCardAuthStore,
		logger:                    logger,
	}
}

// AllocateDevices 授权设备给企业，支持 list/filter 两种模式
func (s *Service) AllocateDevices(ctx context.Context, enterpriseID uint, req *dto.AllocateDevicesReq) (*dto.AllocateDevicesResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	// 验证企业存在
	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	// 根据选取模式解析候选设备号列表
	deviceNos, err := s.resolveDeviceNosForAllocate(ctx, req)
	if err != nil {
		return nil, err
	}

	// 查询所有设备
	var devices []model.Device
	if err := s.db.WithContext(ctx).Where("virtual_no IN ?", deviceNos).Find(&devices).Error; err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询设备信息失败")
	}

	deviceMap := make(map[string]*model.Device)
	deviceIDs := make([]uint, 0, len(devices))
	for i := range devices {
		deviceMap[devices[i].VirtualNo] = &devices[i]
		deviceIDs = append(deviceIDs, devices[i].ID)
	}

	// 获取当前用户的店铺ID（用于验证设备所有权）
	currentShopID := middleware.GetShopIDFromContext(ctx)
	userType := middleware.GetUserTypeFromContext(ctx)

	// 检查设备是否已存在有效授权（不区分企业）
	activeAuthEnterpriseMap, err := s.enterpriseDeviceAuthStore.GetActiveAuthEnterpriseByDeviceIDs(ctx, deviceIDs)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询已有授权失败")
	}

	resp := &dto.AllocateDevicesResp{
		FailedItems:       make([]dto.FailedDeviceItem, 0),
		AuthorizedDevices: make([]dto.AuthorizedDeviceItem, 0),
	}

	devicesToAllocate := make([]*model.Device, 0)
	seenDeviceNos := make(map[string]struct{}, len(deviceNos))
	for _, deviceNo := range deviceNos {
		if _, exists := seenDeviceNos[deviceNo]; exists {
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    "请求中设备号重复",
			})
			continue
		}
		seenDeviceNos[deviceNo] = struct{}{}

		device, exists := deviceMap[deviceNo]
		if !exists {
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    "设备不存在",
			})
			continue
		}

		// 验证设备状态（必须是"已分销"状态）
		if device.Status != 2 {
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    "设备状态不正确，必须是已分销状态",
			})
			continue
		}

		// 验证设备所有权（除非是超级管理员或平台用户）
		if userType == constants.UserTypeAgent {
			if device.ShopID == nil || *device.ShopID != currentShopID {
				resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
					VirtualNo: deviceNo,
					Reason:    "无权操作此设备",
				})
				continue
			}
		}

		// 检查是否已授权（同企业 / 其他企业）
		if authEnterpriseID, exists := activeAuthEnterpriseMap[device.ID]; exists {
			reason := "设备已授权给其他企业"
			if authEnterpriseID == enterpriseID {
				reason = "设备已授权给此企业"
			}
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    reason,
			})
			continue
		}

		devicesToAllocate = append(devicesToAllocate, device)
	}

	// 在事务中处理授权
	if len(devicesToAllocate) > 0 {
		err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
			now := time.Now()
			authorizerType := userType

			// 1. 创建设备授权记录（逐条处理，避免并发冲突导致整批失败）
			deviceAuthIDMap := make(map[uint]uint, len(devicesToAllocate))
			successDevices := make([]*model.Device, 0, len(devicesToAllocate))
			for _, device := range devicesToAllocate {
				deviceAuth := &model.EnterpriseDeviceAuthorization{
					EnterpriseID:   enterpriseID,
					DeviceID:       device.ID,
					AuthorizedBy:   currentUserID,
					AuthorizedAt:   now,
					AuthorizerType: authorizerType,
					Remark:         req.Remark,
				}

				if err := tx.Create(deviceAuth).Error; err != nil {
					if isUniqueConstraintViolation(err, "uq_active_device_auth") {
						reason := "设备已授权给其他企业"

						var existingAuth model.EnterpriseDeviceAuthorization
						queryErr := tx.Select("enterprise_id").
							Where("device_id = ? AND revoked_at IS NULL", device.ID).
							First(&existingAuth).Error
						if queryErr == nil && existingAuth.EnterpriseID == enterpriseID {
							reason = "设备已授权给此企业"
						}
						if queryErr != nil && !stderrors.Is(queryErr, gorm.ErrRecordNotFound) {
							return errors.Wrap(errors.CodeInternalError, queryErr, "查询冲突授权记录失败")
						}

						resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
							VirtualNo: device.VirtualNo,
							Reason:    reason,
						})
						continue
					}
					return errors.Wrap(errors.CodeInternalError, err, "创建设备授权记录失败")
				}

				deviceAuthIDMap[device.ID] = deviceAuth.ID
				successDevices = append(successDevices, device)
			}

			// 2. 查询所有设备绑定的卡
			deviceIDsToQuery := make([]uint, 0, len(successDevices))
			for _, device := range successDevices {
				deviceIDsToQuery = append(deviceIDsToQuery, device.ID)
			}

			var bindings []model.DeviceSimBinding
			if len(deviceIDsToQuery) > 0 {
				if err := tx.Where("device_id IN ? AND bind_status = 1", deviceIDsToQuery).Find(&bindings).Error; err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "查询设备绑定卡失败")
				}
			}

			// 3. 为每张绑定的卡创建授权记录
			if len(bindings) > 0 {
				cardAuths := make([]*model.EnterpriseCardAuthorization, 0, len(bindings))
				for _, binding := range bindings {
					deviceAuthID := deviceAuthIDMap[binding.DeviceID]
					cardAuths = append(cardAuths, &model.EnterpriseCardAuthorization{
						EnterpriseID:   enterpriseID,
						CardID:         binding.IotCardID,
						DeviceAuthID:   &deviceAuthID,
						AuthorizedBy:   currentUserID,
						AuthorizedAt:   now,
						AuthorizerType: authorizerType,
						Remark:         req.Remark,
					})
				}

				if err := tx.Create(cardAuths).Error; err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "创建卡授权记录失败")
				}
			}

			// 4. 统计每个设备的绑定卡数量
			deviceCardCount := make(map[uint]int)
			for _, binding := range bindings {
				deviceCardCount[binding.DeviceID]++
			}

			// 5. 构建响应
			for _, device := range successDevices {
				resp.AuthorizedDevices = append(resp.AuthorizedDevices, dto.AuthorizedDeviceItem{
					DeviceID:  device.ID,
					VirtualNo: device.VirtualNo,
					CardCount: deviceCardCount[device.ID],
				})
			}

			return nil
		})

		if err != nil {
			return nil, err
		}
	}

	resp.SuccessCount = len(resp.AuthorizedDevices)
	resp.FailCount = len(resp.FailedItems)
	return resp, nil
}

func isUniqueConstraintViolation(err error, constraintName string) bool {
	var pgErr *pgconn.PgError
	if stderrors.As(err, &pgErr) {
		if pgErr.Code != "23505" {
			return false
		}
		if constraintName == "" {
			return true
		}
		return pgErr.ConstraintName == constraintName
	}
	return false
}

// resolveDeviceNosForAllocate 根据选取模式解析授权候选设备号列表
// list 模式：直接使用请求中的 device_nos
// filter 模式：按过滤条件从 store 查询，代理用户的店铺限制由 ApplyShopFilter 自动应用
func (s *Service) resolveDeviceNosForAllocate(ctx context.Context, req *dto.AllocateDevicesReq) ([]string, error) {
	if req.SelectionType == "list" {
		return req.DeviceNos, nil
	}
	filters := make(map[string]any)
	if req.VirtualNo != "" {
		filters["virtual_no"] = req.VirtualNo
	}
	if req.BatchNo != "" {
		filters["batch_no"] = req.BatchNo
	}
	if req.ShopID != nil {
		filters["shop_id"] = req.ShopID
	}
	devices, err := s.deviceStore.GetByFilters(ctx, filters)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "按条件查询设备失败")
	}
	nos := make([]string, 0, len(devices))
	for _, d := range devices {
		nos = append(nos, d.VirtualNo)
	}
	return nos, nil
}

// resolveDeviceNosForRecall 根据选取模式解析收回候选设备号列表
// filter 模式仅返回当前企业有效授权的设备（通过子查询限定）
func (s *Service) resolveDeviceNosForRecall(ctx context.Context, enterpriseID uint, req *dto.RecallDevicesReq) ([]string, error) {
	if req.SelectionType == "list" {
		return req.DeviceNos, nil
	}
	// filter 模式：只查询已授权给该企业的设备
	filters := make(map[string]any)
	if req.VirtualNo != "" {
		filters["virtual_no"] = req.VirtualNo
	}
	if req.BatchNo != "" {
		filters["batch_no"] = req.BatchNo
	}
	// 仅返回有效授权给该企业的设备
	filters["authorized_enterprise_id"] = enterpriseID
	devices, err := s.deviceStore.GetByFilters(ctx, filters)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "按条件查询设备失败")
	}
	nos := make([]string, 0, len(devices))
	for _, d := range devices {
		nos = append(nos, d.VirtualNo)
	}
	return nos, nil
}

// RecallDevices 撤销设备授权，支持 list/filter 两种模式
func (s *Service) RecallDevices(ctx context.Context, enterpriseID uint, req *dto.RecallDevicesReq) (*dto.RecallDevicesResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	// 验证企业存在
	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	// 根据选取模式解析候选设备号列表
	deviceNos, err := s.resolveDeviceNosForRecall(ctx, enterpriseID, req)
	if err != nil {
		return nil, err
	}

	// 查询设备
	var devices []model.Device
	if err := s.db.WithContext(ctx).Where("virtual_no IN ?", deviceNos).Find(&devices).Error; err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询设备信息失败")
	}

	deviceMap := make(map[string]*model.Device)
	deviceIDs := make([]uint, 0, len(devices))
	for i := range devices {
		deviceMap[devices[i].VirtualNo] = &devices[i]
		deviceIDs = append(deviceIDs, devices[i].ID)
	}

	// 检查授权状态
	existingAuths, err := s.enterpriseDeviceAuthStore.GetActiveAuthsByDeviceIDs(ctx, enterpriseID, deviceIDs)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询授权状态失败")
	}

	resp := &dto.RecallDevicesResp{
		FailedItems: make([]dto.FailedDeviceItem, 0),
	}

	deviceAuthsToRevoke := make([]uint, 0)
	for _, deviceNo := range deviceNos {
		device, exists := deviceMap[deviceNo]
		if !exists {
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    "设备不存在",
			})
			continue
		}

		if !existingAuths[device.ID] {
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    "设备未授权给此企业",
			})
			continue
		}

		// 获取授权记录ID
		auth, err := s.enterpriseDeviceAuthStore.GetByDeviceID(ctx, device.ID)
		if err != nil || auth.EnterpriseID != enterpriseID {
			resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
				VirtualNo: deviceNo,
				Reason:    "授权记录不存在",
			})
			continue
		}

		deviceAuthsToRevoke = append(deviceAuthsToRevoke, auth.ID)
	}

	// 在事务中处理撤销
	if len(deviceAuthsToRevoke) > 0 {
		err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
			// 1. 撤销设备授权
			if err := s.enterpriseDeviceAuthStore.RevokeByIDs(ctx, deviceAuthsToRevoke, currentUserID); err != nil {
				return errors.Wrap(errors.CodeInternalError, err, "撤销设备授权失败")
			}

			// 2. 级联撤销卡授权
			for _, authID := range deviceAuthsToRevoke {
				if err := s.enterpriseCardAuthStore.RevokeByDeviceAuthID(ctx, authID, currentUserID); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "撤销卡授权失败")
				}
			}

			return nil
		})

		if err != nil {
			return nil, err
		}
	}

	resp.SuccessCount = len(deviceAuthsToRevoke)
	resp.FailCount = len(resp.FailedItems)
	return resp, nil
}

// ListDevices 查询企业授权设备列表（后台管理）
func (s *Service) ListDevices(ctx context.Context, enterpriseID uint, req *dto.EnterpriseDeviceListReq) (*dto.EnterpriseDeviceListResp, error) {
	// 验证企业存在
	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	// 查询授权记录
	opts := postgres.DeviceAuthListOptions{
		EnterpriseID:   &enterpriseID,
		IncludeRevoked: false,
		Page:           req.Page,
		PageSize:       req.PageSize,
	}

	auths, total, err := s.enterpriseDeviceAuthStore.ListByEnterprise(ctx, opts)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询授权记录失败")
	}

	if len(auths) == 0 {
		return &dto.EnterpriseDeviceListResp{
			List:  make([]dto.EnterpriseDeviceItem, 0),
			Total: 0,
		}, nil
	}

	// 收集设备ID
	deviceIDs := make([]uint, 0, len(auths))
	authMap := make(map[uint]*model.EnterpriseDeviceAuthorization)
	for _, auth := range auths {
		deviceIDs = append(deviceIDs, auth.DeviceID)
		authMap[auth.DeviceID] = auth
	}

	// 查询设备信息
	var devices []model.Device
	query := s.db.WithContext(ctx).Where("id IN ?", deviceIDs)
	if req.VirtualNo != "" {
		query = query.Where("virtual_no LIKE ?", "%"+req.VirtualNo+"%")
	}
	if err := query.Find(&devices).Error; err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询设备信息失败")
	}

	// 统计每个设备的绑定卡数量
	var bindings []model.DeviceSimBinding
	if err := s.db.WithContext(ctx).
		Where("device_id IN ? AND bind_status = 1", deviceIDs).
		Find(&bindings).Error; err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询设备绑定卡失败")
	}

	cardCountMap := make(map[uint]int)
	for _, binding := range bindings {
		cardCountMap[binding.DeviceID]++
	}

	// 构建响应
	items := make([]dto.EnterpriseDeviceItem, 0, len(devices))
	for _, device := range devices {
		auth := authMap[device.ID]
		items = append(items, dto.EnterpriseDeviceItem{
			DeviceID:     device.ID,
			VirtualNo:    device.VirtualNo,
			DeviceName:   device.DeviceName,
			DeviceModel:  device.DeviceModel,
			CardCount:    cardCountMap[device.ID],
			AuthorizedAt: auth.AuthorizedAt,
		})
	}

	return &dto.EnterpriseDeviceListResp{
		List:  items,
		Total: total,
	}, nil
}
