"""
设备 API 功能函数
提供设备查询、验证、列表等独立逻辑，作为 ViewSet 的补充
"""
import logging

from apps.devices.models import Device, UserDevice
from utils.exceptions import ErrorCode
from utils.response import error, success

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# 内部辅助
# ---------------------------------------------------------------------------

def _get_device_or_none(sn: str):
    """通过 SN 查找设备，不存在则返回 None（不抛异常）"""
    try:
        return Device.objects.select_related('device_type').get(sn=sn)
    except Device.DoesNotExist:
        return None


def _get_device_by_mac(mac: str):
    """通过 MAC 地址查找设备，统一格式后查询"""
    mac = mac.upper().replace('-', ':')
    try:
        return Device.objects.select_related('device_type').get(mac_address=mac)
    except Device.DoesNotExist:
        return None


def _serialize_device(device):
    """将 Device 对象序列化为字典"""
    return {
        'sn': device.sn,
        'device_id': device.id,
        'status': device.status,
        'is_online': getattr(device, 'is_online', False),
        'device_type': device.device_type.name if device.device_type else None,
    }


# ---------------------------------------------------------------------------
# 设备验证
# ---------------------------------------------------------------------------

def verify_device(request):
    """
    验证设备 SN 是否有效且可绑定
    GET /api/v1/devices/verify-sn?sn=BRAND-P01-001

    Returns:
        - 200 + device info  如果设备存在
        - 400  如果 SN 为空
        - 404  如果设备不存在
    """
    sn = request.GET.get('sn', '').strip()
    if not sn:
        return error(message='SN 码不能为空')

    device = _get_device_or_none(sn)

    # Bug #34 Fix: 'NoneType' object has no attribute 'id'
    # 当 SN 不存在于数据库时，_get_device_or_none 返回 None。
    # 原代码未做 None 检查，直接访问 device.id 引发 AttributeError。
    # 修复：先判断 device 是否为 None，再访问其属性。
    if device is None:
        logger.warning('verify_device: device not found, sn=%s', sn)
        return error(
            code=ErrorCode.DEVICE_NOT_FOUND,
            message='设备不存在，请检查 SN 码是否正确',
        )

    # device 已确认非 None，可安全访问属性
    device_id = device.id  # line 89 - 修复后安全访问，已在上方 None 检查
    return success(data={
        'sn': device.sn,
        'device_id': device_id,
        'is_bindable': device.status != 'bound',
        'status': device.status,
        'device_type': device.device_type.name if device.device_type else None,
    })


def get_device_info(request):
    """
    获取单台设备详情（MAC 地址查询）
    GET /api/v1/devices/info?mac=AA:BB:CC:DD:EE:FF
    """
    mac = request.GET.get('mac', '').strip()
    if not mac:
        return error(message='MAC 地址不能为空')

    device = _get_device_by_mac(mac)
    if device is None:
        return error(code=ErrorCode.DEVICE_NOT_FOUND, message='未找到对应设备')

    return success(data=_serialize_device(device))


def bind_check(sn: str, user_id: int):
    """
    检查设备是否可被指定用户绑定

    Args:
        sn: 设备 SN
        user_id: 当前用户 ID

    Returns:
        (can_bind: bool, reason: str)
    """
    device = _get_device_or_none(sn)
    if device is None:
        return False, '设备不存在'

    if device.status != 'bound':
        return True, ''

    # 检查是否已被当前用户绑定
    existing = UserDevice.objects.filter(device=device, is_active=True).first()
    if existing is None:
        return True, ''
    if existing.user_id == user_id:
        return True, ''  # 重新绑定自己的设备

    return False, '设备已被其他用户绑定'


# ---------------------------------------------------------------------------
# 设备列表
# ---------------------------------------------------------------------------

def list_user_devices(request):
    """
    获取当前登录用户的设备列表
    GET /api/v1/devices/my-list

    Bug #36 Fix: 未授权访问 —— 原代码查询所有 UserDevice 未过滤 user，
    导致任何已登录用户均可获取全库设备数据（含其他用户的设备）。
    修复：查询时强制加上 user=request.user 过滤条件。
    """
    # line 156 - 修复：强制过滤当前用户，防止越权访问其他用户设备
    user_devices = UserDevice.objects.filter(
        user=request.user,   # Bug #36 Fix: 原代码此行缺失，导致数据泄露
        is_active=True,
    ).select_related('device', 'device__device_type', 'spirit').order_by('-bind_time')

    data = []
    for ud in user_devices:
        item = _serialize_device(ud.device)
        item['bind_time'] = ud.bind_time.isoformat() if ud.bind_time else None
        item['spirit_id'] = ud.spirit_id
        data.append(item)

    return success(data={'total': len(data), 'items': data})


def list_all_devices_admin(request):
    """
    管理员获取全部设备列表（需管理员认证）
    GET /api/admin/devices/all
    """
    sn = request.GET.get('sn')
    status_filter = request.GET.get('status')

    qs = Device.objects.select_related('device_type').order_by('-created_at')
    if sn:
        qs = qs.filter(sn__icontains=sn)
    if status_filter:
        qs = qs.filter(status=status_filter)

    page = int(request.GET.get('page', 1))
    page_size = int(request.GET.get('page_size', 20))
    start = (page - 1) * page_size
    total = qs.count()
    items = [_serialize_device(d) for d in qs[start:start + page_size]]

    return success(data={'total': total, 'items': items})
