"""
设备模块视图 - App端
"""
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from drf_spectacular.utils import extend_schema

from utils.response import success, error
from utils.exceptions import ErrorCode
from apps.admins.authentication import AppJWTAuthentication
from .models import Device, UserDevice, DeviceType, DeviceSettings, DeviceWifi, RoleMemory
from .serializers import (
    DeviceSerializer,
    UserDeviceSerializer,
    BindDeviceSerializer,
    DeviceVerifySerializer,
    DeviceTypeSerializer,
    DeviceDetailSerializer,
    DeviceSettingsUpdateSerializer,
    DeviceReportStatusSerializer,
    RoleMemorySerializer,
    RoleMemorySettingsUpdateSerializer,
    RoleMemoryAgentUpdateSerializer,
    RoleMemoryMemoryUpdateSerializer,
)


@extend_schema(tags=['设备'])
class DeviceViewSet(viewsets.ViewSet):
    """设备视图集（App端）"""

    authentication_classes = [AppJWTAuthentication]
    permission_classes = [IsAuthenticated]

    @action(detail=False, methods=['get'], url_path='query-by-mac')
    def query_by_mac(self, request):
        """
        通过MAC地址查询设备信息及绑定状态
        GET /api/v1/devices/query-by-mac?mac=AA:BB:CC:DD:EE:FF
        返回 bound_by_me / bound_by_other / unbound
        """
        mac = request.query_params.get('mac', '')
        if not mac:
            return error(message='MAC地址不能为空')

        # 统一格式
        mac = mac.upper().replace('-', ':')

        try:
            device = Device.objects.select_related('device_type').get(mac_address=mac)
        except Device.DoesNotExist:
            return error(
                code=404,
                message='未找到对应的设备，请检查MAC地址是否正确或设备是否已完成入库',
                status_code=status.HTTP_404_NOT_FOUND
            )

        # 判断绑定归属
        bind_status = 'unbound'
        if device.status == 'bound':
            owner = UserDevice.objects.filter(
                device=device, is_active=True
            ).first()
            if owner and owner.user == request.user:
                bind_status = 'bound_by_me'
            elif owner:
                bind_status = 'bound_by_other'

        return success(data={
            'sn': device.sn,
            'mac_address': device.mac_address,
            'device_type': DeviceTypeSerializer(device.device_type).data if device.device_type else None,
            'status': device.status,
            'bind_status': bind_status,
        })

    @action(detail=False, methods=['get'])
    def latest(self, request):
        """
        获取用户最近使用的设备
        GET /api/v1/devices/latest
        """
        devices = list(
            UserDevice.objects.filter(
                user=request.user,
                is_active=True
            ).select_related('device', 'device__device_type', 'spirit', 'role_memory', 'role_memory__device_type')
            .order_by('-bind_time')[:1]
        )
        if not devices:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='暂无绑定设备')
        latest = devices[0]
        return success(data=UserDeviceSerializer(latest).data)

    @action(detail=False, methods=['post'])
    def verify(self, request):
        """
        验证设备SN
        POST /api/v1/devices/verify
        """
        serializer = DeviceVerifySerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        sn = serializer.validated_data['sn']

        try:
            device = Device.objects.select_related('device_type').get(sn=sn)
        except Device.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='设备不存在')

        # 检查是否已被绑定
        is_bindable = device.status != 'bound'

        return success(data={
            'sn': device.sn,
            'is_bindable': is_bindable,
            'device_type': DeviceTypeSerializer(device.device_type).data if device.device_type else None
        })

    @action(detail=False, methods=['post'])
    def bind(self, request):
        """
        绑定设备
        POST /api/v1/devices/bind
        """
        serializer = BindDeviceSerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        sn = serializer.validated_data['sn']
        spirit_id = serializer.validated_data.get('spirit_id')

        try:
            device = Device.objects.select_related('device_type').get(sn=sn)
        except Device.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='设备不存在')

        # 检查是否已被绑定
        if device.status == 'bound':
            # 检查是否是当前用户绑定的
            existing = UserDevice.objects.filter(device=device, is_active=True).first()
            if existing and existing.user != request.user:
                return error(code=ErrorCode.DEVICE_ALREADY_BOUND, message='设备已被其他用户绑定')

        # 检查是否已有绑定关系（重复绑定时复用已有的角色记忆）
        existing_binding = UserDevice.objects.filter(
            user=request.user, device=device
        ).select_related('role_memory').first()

        # 普通电子吧唧(DZBJ-OFF)不需要角色记忆
        product_code = device.device_type.product_code if device.device_type else ''
        needs_role_memory = product_code != 'DZBJ-OFF'

        if not needs_role_memory:
            role_memory = None
        elif existing_binding and existing_binding.role_memory:
            # 已有绑定且有角色记忆 → 复用，不重复创建
            role_memory = existing_binding.role_memory
            role_memory.is_bound = True
            role_memory.save(update_fields=['is_bound', 'updated_at'])
        elif device.device_type:
            # 首次绑定 → 创建新角色记忆
            role_memory = RoleMemory.objects.create(
                user=request.user,
                device_type=device.device_type,
                is_bound=True,
                prompt=device.device_type.default_prompt,
                voice_id=device.device_type.default_voice_id,
            )
        else:
            role_memory = None

        # 创建或更新绑定关系
        user_device, created = UserDevice.objects.update_or_create(
            user=request.user,
            device=device,
            defaults={
                'spirit_id': spirit_id,
                'role_memory': role_memory,
                'is_active': True
            }
        )

        # 更新设备状态和可选的设备名称
        device_name = request.data.get('device_name')
        if device_name:
            device.name = device_name
        device.status = 'bound'
        device.save()

        return success(
            data=UserDeviceSerializer(user_device).data,
            message='绑定成功' if created else '更新绑定成功'
        )

    @action(detail=False, methods=['get'])
    def my_devices(self, request):
        """
        我的设备列表
        GET /api/v1/devices/my_devices
        """
        user_devices = UserDevice.objects.filter(
            user=request.user,
            is_active=True
        ).select_related('device', 'device__device_type', 'spirit', 'role_memory', 'role_memory__device_type')

        serializer = UserDeviceSerializer(user_devices, many=True)
        return success(data=serializer.data)

    @action(detail=True, methods=['delete'])
    def unbind(self, request, pk=None):
        """
        解绑设备
        DELETE /api/v1/devices/{id}/unbind
        """
        try:
            user_device = UserDevice.objects.select_related('role_memory').get(
                id=pk, user=request.user
            )
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        # 更新绑定状态
        user_device.is_active = False
        user_device.save()

        # 将关联的角色记忆标记为闲置
        if user_device.role_memory:
            user_device.role_memory.is_bound = False
            user_device.role_memory.save(update_fields=['is_bound', 'updated_at'])

        # 检查设备是否还有其他活跃绑定
        active_bindings = UserDevice.objects.filter(device=user_device.device, is_active=True).count()
        if active_bindings == 0:
            user_device.device.status = 'out_stock'
            user_device.device.save()

        return success(message='解绑成功')

    @action(detail=True, methods=['put'], url_path='update-spirit')
    def update_spirit(self, request, pk=None):
        """
        更新设备绑定的智能体
        PUT /api/v1/devices/{id}/update-spirit
        """
        try:
            user_device = UserDevice.objects.get(id=pk, user=request.user, is_active=True)
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        spirit_id = request.data.get('spirit_id')
        user_device.spirit_id = spirit_id
        user_device.save()

        return success(data=UserDeviceSerializer(user_device).data, message='更新成功')

    @action(detail=True, methods=['get'], url_path='detail')
    def device_detail(self, request, pk=None):
        """
        获取设备详情
        GET /api/v1/devices/{user_device_id}/detail/
        pk 为 UserDevice 的 ID
        """
        try:
            user_device = UserDevice.objects.select_related(
                'device', 'spirit', 'role_memory', 'role_memory__device_type'
            ).get(id=pk, user=request.user, is_active=True)
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        device = user_device.device
        serializer = DeviceDetailSerializer(
            device, context={'user_device': user_device}
        )
        return success(data=serializer.data)

    @action(detail=True, methods=['put'], url_path='settings')
    def update_settings(self, request, pk=None):
        """
        更新设备设置
        PUT /api/v1/devices/{user_device_id}/settings/
        """
        try:
            user_device = UserDevice.objects.select_related('device').get(
                id=pk, user=request.user, is_active=True
            )
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        serializer = DeviceSettingsUpdateSerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        device = user_device.device
        settings_obj, _ = DeviceSettings.objects.get_or_create(device=device)

        for field, value in serializer.validated_data.items():
            setattr(settings_obj, field, value)
        settings_obj.save()

        return success(message='设置已保存')

    @action(detail=True, methods=['post'], url_path='wifi')
    def configure_wifi(self, request, pk=None):
        """
        配置设备WiFi
        POST /api/v1/devices/{user_device_id}/wifi/
        """
        try:
            user_device = UserDevice.objects.select_related('device').get(
                id=pk, user=request.user, is_active=True
            )
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        ssid = request.data.get('ssid')
        if not ssid:
            return error(message='WiFi名称不能为空')

        device = user_device.device

        # 将其他 WiFi 标记为未连接
        DeviceWifi.objects.filter(device=device).update(is_connected=False)

        # 创建或更新当前 WiFi 记录
        wifi, _ = DeviceWifi.objects.update_or_create(
            device=device,
            ssid=ssid,
            defaults={'is_connected': True}
        )

        # TODO: 通过设备通信协议下发 WiFi 配置（password 不存库）

        return success(message='WiFi 配置成功')

    @action(
        detail=False, methods=['get'],
        url_path='stories',
        authentication_classes=[], permission_classes=[AllowAny]
    )
    def stories_by_mac(self, request):
        """
        获取设备关联用户的随机故事（公开接口，无需认证）
        GET /api/v1/devices/stories/?mac_address=AA:BB:CC:DD:EE:FF
        供 hw-ws-service 调用。
        优先返回用户自己的故事，无则兜底返回系统默认故事（is_default=True）。
        """
        mac = request.query_params.get('mac_address', '').strip()
        if not mac:
            return error(message='mac_address 参数不能为空')

        mac = mac.upper().replace('-', ':')

        from apps.stories.models import Story
        story = None

        # 1. 尝试查找设备 → 绑定用户 → 用户故事
        try:
            device = Device.objects.get(mac_address=mac)
            user_device = (
                UserDevice.objects
                .filter(device=device, is_active=True, bind_type='owner')
                .select_related('user')
                .first()
            )
            if user_device:
                story = (
                    Story.objects
                    .filter(user=user_device.user)
                    .exclude(audio_url='')
                    .order_by('?')
                    .first()
                )
        except Device.DoesNotExist:
            pass

        # 2. 兜底：设备不存在/未绑定/用户无故事 → 使用系统默认故事
        if not story:
            story = (
                Story.objects
                .filter(is_default=True)
                .exclude(audio_url='')
                .order_by('?')
                .first()
            )
        if not story:
            return error(
                code=ErrorCode.STORY_NOT_FOUND,
                message='暂无可播放的故事',
                status_code=status.HTTP_404_NOT_FOUND
            )

        return success(data={
            'title': story.title,
            'audio_url': story.audio_url,
            'opus_url': story.opus_url,
            'intro_opus_data': story.intro_opus_data,
        })

    @action(
        detail=False, methods=['get'],
        url_path='music',
        authentication_classes=[], permission_classes=[AllowAny]
    )
    def music_by_mac(self, request):
        """
        获取设备关联用户的随机音乐（公开接口，无需认证）
        GET /api/v1/devices/music/?mac_address=AA:BB:CC:DD:EE:FF
        供 hw-ws-service 调用。
        优先返回用户自己的音乐，无则兜底返回系统默认曲目（is_default=True）。
        """
        mac = request.query_params.get('mac_address', '').strip()
        if not mac:
            return error(message='mac_address 参数不能为空')

        mac = mac.upper().replace('-', ':')

        from apps.music.models import Track
        track = None

        # 1. 尝试查找设备 → 绑定用户 → 用户音乐
        try:
            device = Device.objects.get(mac_address=mac)
            user_device = (
                UserDevice.objects
                .filter(device=device, is_active=True, bind_type='owner')
                .select_related('user')
                .first()
            )
            if user_device:
                track = (
                    Track.objects
                    .filter(user=user_device.user, generation_status='completed')
                    .exclude(audio_url='')
                    .order_by('?')
                    .first()
                )
        except Device.DoesNotExist:
            pass

        # 2. 兜底：设备不存在/未绑定/用户无音乐 → 使用系统默认曲目
        if not track:
            track = (
                Track.objects
                .filter(is_default=True, generation_status='completed')
                .exclude(audio_url='')
                .order_by('?')
                .first()
            )
        if not track:
            return error(
                code=ErrorCode.TRACK_NOT_FOUND,
                message='暂无可播放的音乐',
                status_code=status.HTTP_404_NOT_FOUND
            )

        return success(data={
            'title': track.title,
            'audio_url': track.audio_url,
            'opus_url': track.opus_url,
            'intro_opus_data': track.intro_opus_data,
            'cover_url': track.cover_url,
            'duration': track.duration,
        })

    @action(detail=False, methods=['post'], url_path='report-status',
            authentication_classes=[], permission_classes=[AllowAny])
    def report_status(self, request):
        """
        设备状态上报（硬件端调用，无需认证）
        POST /api/v1/devices/report-status
        """
        serializer = DeviceReportStatusSerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        data = serializer.validated_data
        mac = data.pop('mac_address')

        try:
            device = Device.objects.get(mac_address=mac)
        except Device.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='设备不存在')

        # 更新 Device 表字段
        device_fields_updated = False
        for field in ('is_online', 'battery', 'firmware_version'):
            if field in data:
                setattr(device, field, data[field])
                device_fields_updated = True

        if data.get('is_online'):
            from django.utils import timezone
            device.last_online_at = timezone.now()
            device_fields_updated = True

        if device_fields_updated:
            device.save()

        # 更新 DeviceSettings 表字段（仅 volume / brightness）
        settings_data = {k: data[k] for k in ('volume', 'brightness') if k in data}
        if settings_data:
            settings_obj, _ = DeviceSettings.objects.get_or_create(device=device)
            for field, value in settings_data.items():
                setattr(settings_obj, field, value)
            settings_obj.save()

        return success(
            data={'device_id': device.id, 'sn': device.sn},
            message='状态上报成功'
        )

    # ==================== 角色记忆相关端点 ====================

    @action(detail=True, methods=['get'], url_path='role-memory')
    def role_memory_detail(self, request, pk=None):
        """
        获取当前设备的角色记忆
        GET /api/v1/devices/{user_device_id}/role-memory/
        """
        try:
            user_device = UserDevice.objects.select_related(
                'role_memory', 'role_memory__device_type'
            ).get(id=pk, user=request.user, is_active=True)
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        if not user_device.role_memory:
            return error(code=ErrorCode.ROLE_MEMORY_NOT_FOUND, message='该设备暂无角色记忆')

        return success(data=RoleMemorySerializer(user_device.role_memory).data)

    @action(detail=True, methods=['put'], url_path='role-memory/settings')
    def role_memory_settings(self, request, pk=None):
        """
        更新角色记忆-设备设置
        PUT /api/v1/devices/{user_device_id}/role-memory/settings/
        """
        try:
            user_device = UserDevice.objects.select_related('role_memory').get(
                id=pk, user=request.user, is_active=True
            )
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        if not user_device.role_memory:
            return error(code=ErrorCode.ROLE_MEMORY_NOT_FOUND, message='该设备暂无角色记忆')

        serializer = RoleMemorySettingsUpdateSerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        rm = user_device.role_memory
        for field, value in serializer.validated_data.items():
            setattr(rm, field, value)
        rm.save()

        return success(data=RoleMemorySerializer(rm).data, message='设置已保存')

    @action(detail=True, methods=['put'], url_path='role-memory/agent')
    def role_memory_agent(self, request, pk=None):
        """
        更新角色记忆-Agent信息
        PUT /api/v1/devices/{user_device_id}/role-memory/agent/
        """
        try:
            user_device = UserDevice.objects.select_related('role_memory').get(
                id=pk, user=request.user, is_active=True
            )
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        if not user_device.role_memory:
            return error(code=ErrorCode.ROLE_MEMORY_NOT_FOUND, message='该设备暂无角色记忆')

        serializer = RoleMemoryAgentUpdateSerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        rm = user_device.role_memory
        for field, value in serializer.validated_data.items():
            setattr(rm, field, value)
        rm.save()

        return success(data=RoleMemorySerializer(rm).data, message='Agent信息已更新')

    @action(detail=True, methods=['put'], url_path='role-memory/memory')
    def role_memory_summary(self, request, pk=None):
        """
        更新角色记忆-聊天记忆摘要
        PUT /api/v1/devices/{user_device_id}/role-memory/memory/
        """
        try:
            user_device = UserDevice.objects.select_related('role_memory').get(
                id=pk, user=request.user, is_active=True
            )
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        if not user_device.role_memory:
            return error(code=ErrorCode.ROLE_MEMORY_NOT_FOUND, message='该设备暂无角色记忆')

        serializer = RoleMemoryMemoryUpdateSerializer(data=request.data)
        if not serializer.is_valid():
            return error(message=str(serializer.errors))

        user_device.role_memory.memory_summary = serializer.validated_data['memory_summary']
        user_device.role_memory.save(update_fields=['memory_summary', 'updated_at'])

        return success(message='聊天记忆已更新')

    @action(detail=False, methods=['get'], url_path='role-memories')
    def role_memory_list(self, request):
        """
        获取角色记忆列表
        GET /api/v1/devices/role-memories/?device_type_id=1&is_bound=false
        """
        qs = RoleMemory.objects.filter(user=request.user).select_related('device_type')
        device_type_id = request.query_params.get('device_type_id')
        if device_type_id:
            qs = qs.filter(device_type_id=device_type_id)
        is_bound = request.query_params.get('is_bound')
        if is_bound is not None:
            qs = qs.filter(is_bound=is_bound.lower() == 'true')
        return success(data=RoleMemorySerializer(qs, many=True).data)

    @action(detail=True, methods=['put'], url_path='switch-role-memory')
    def switch_role_memory(self, request, pk=None):
        """
        切换设备的角色记忆
        PUT /api/v1/devices/{user_device_id}/switch-role-memory/
        body: { "role_memory_id": 5 }
        """
        try:
            user_device = UserDevice.objects.select_related(
                'device', 'device__device_type', 'role_memory'
            ).get(id=pk, user=request.user, is_active=True)
        except UserDevice.DoesNotExist:
            return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在')

        role_memory_id = request.data.get('role_memory_id')
        if not role_memory_id:
            return error(message='role_memory_id 不能为空')

        try:
            new_rm = RoleMemory.objects.get(id=role_memory_id, user=request.user)
        except RoleMemory.DoesNotExist:
            return error(code=ErrorCode.ROLE_MEMORY_NOT_FOUND, message='该设备暂无角色记忆')

        # 校验: 目标记忆必须是同一设备类型
        if user_device.device.device_type_id and new_rm.device_type_id != user_device.device.device_type_id:
            return error(code=ErrorCode.ROLE_MEMORY_TYPE_MISMATCH, message='只能切换到同类型设备的角色记忆')

        # 校验: 目标记忆必须是闲置状态
        if new_rm.is_bound:
            return error(code=ErrorCode.ROLE_MEMORY_ALREADY_BOUND, message='该角色记忆正在被其他设备使用')

        # 执行切换：旧记忆标记闲置，新记忆标记绑定
        old_rm = user_device.role_memory
        if old_rm:
            old_rm.is_bound = False
            old_rm.save(update_fields=['is_bound', 'updated_at'])

        new_rm.is_bound = True
        new_rm.save(update_fields=['is_bound', 'updated_at'])

        user_device.role_memory = new_rm
        user_device.save(update_fields=['role_memory'])

        return success(data=RoleMemorySerializer(new_rm).data, message='切换成功')
