"""
REST API endpoints for mc-webui
"""

import logging
import json
import re
import base64
import time
from datetime import datetime
from io import BytesIO
from flask import Blueprint, jsonify, request, send_file
from app.meshcore import cli, parser
from app.config import config
from app.archiver import manager as archive_manager

logger = logging.getLogger(__name__)

api_bp = Blueprint('api', __name__, url_prefix='/api')

# Simple cache for get_channels() to reduce USB/meshcli calls
# Channels don't change frequently, so caching for 30s is safe
_channels_cache = None
_channels_cache_timestamp = 0
CHANNELS_CACHE_TTL = 30  # seconds


def get_channels_cached(force_refresh=False):
    """
    Get channels with caching to reduce USB/meshcli calls.

    Args:
        force_refresh: If True, bypass cache and fetch fresh data

    Returns:
        Tuple of (success, channels_list)
    """
    global _channels_cache, _channels_cache_timestamp

    current_time = time.time()

    # Return cached data if valid and not forcing refresh
    if (not force_refresh and
        _channels_cache is not None and
        (current_time - _channels_cache_timestamp) < CHANNELS_CACHE_TTL):
        logger.debug(f"Returning cached channels (age: {current_time - _channels_cache_timestamp:.1f}s)")
        return True, _channels_cache

    # Fetch fresh data
    logger.debug("Fetching fresh channels from meshcli")
    success, channels = cli.get_channels()

    if success:
        _channels_cache = channels
        _channels_cache_timestamp = current_time
        logger.debug(f"Channels cached ({len(channels)} channels)")

    return success, channels


def invalidate_channels_cache():
    """Invalidate channels cache (call after add/remove channel)"""
    global _channels_cache, _channels_cache_timestamp
    _channels_cache = None
    _channels_cache_timestamp = 0
    logger.debug("Channels cache invalidated")


@api_bp.route('/messages', methods=['GET'])
def get_messages():
    """
    Get list of messages from specific channel or archive.

    Query parameters:
        limit (int): Maximum number of messages to return
        offset (int): Number of messages to skip from the end
        archive_date (str): View archive for specific date (YYYY-MM-DD format)
        days (int): Show only messages from last N days (live view only)
        channel_idx (int): Filter by channel index (optional)

    Returns:
        JSON with messages list
    """
    try:
        limit = request.args.get('limit', type=int)
        offset = request.args.get('offset', default=0, type=int)
        archive_date = request.args.get('archive_date', type=str)
        days = request.args.get('days', type=int)
        channel_idx = request.args.get('channel_idx', type=int)

        # Validate archive_date format if provided
        if archive_date:
            try:
                datetime.strptime(archive_date, '%Y-%m-%d')
            except ValueError:
                return jsonify({
                    'success': False,
                    'error': f'Invalid date format: {archive_date}. Expected YYYY-MM-DD'
                }), 400

        # Read messages (from archive or live .msgs file)
        messages = parser.read_messages(
            limit=limit,
            offset=offset,
            archive_date=archive_date,
            days=days,
            channel_idx=channel_idx
        )

        return jsonify({
            'success': True,
            'count': len(messages),
            'messages': messages,
            'archive_date': archive_date if archive_date else None,
            'channel_idx': channel_idx
        }), 200

    except Exception as e:
        logger.error(f"Error fetching messages: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/messages', methods=['POST'])
def send_message():
    """
    Send a message to a specific channel.

    JSON body:
        text (str): Message content (required)
        reply_to (str): Username to reply to (optional)
        channel_idx (int): Channel to send to (optional, default: 0)

    Returns:
        JSON with success status
    """
    try:
        data = request.get_json()

        if not data or 'text' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: text'
            }), 400

        text = data['text'].strip()
        if not text:
            return jsonify({
                'success': False,
                'error': 'Message text cannot be empty'
            }), 400

        # MeshCore message length limit (~180-200 bytes for LoRa)
        # Count UTF-8 bytes, not Unicode characters
        byte_length = len(text.encode('utf-8'))
        if byte_length > 200:
            return jsonify({
                'success': False,
                'error': f'Message too long ({byte_length} bytes). Maximum 200 bytes allowed due to LoRa constraints.'
            }), 400

        reply_to = data.get('reply_to')
        channel_idx = data.get('channel_idx', 0)

        # Send message via meshcli
        success, message = cli.send_message(text, reply_to=reply_to, channel_index=channel_idx)

        if success:
            return jsonify({
                'success': True,
                'message': 'Message sent successfully',
                'channel_idx': channel_idx
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error sending message: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/status', methods=['GET'])
def get_status():
    """
    Get device connection status and basic info.

    Returns:
        JSON with status information
    """
    try:
        # Check if device is accessible
        connected = cli.check_connection()

        # Get message count
        message_count = parser.count_messages()

        # Get latest message timestamp
        latest = parser.get_latest_message()
        latest_timestamp = latest['timestamp'] if latest else None

        return jsonify({
            'success': True,
            'connected': connected,
            'device_name': config.MC_DEVICE_NAME,
            'serial_port': config.MC_SERIAL_PORT,
            'message_count': message_count,
            'latest_message_timestamp': latest_timestamp,
            'refresh_interval': config.MC_REFRESH_INTERVAL
        }), 200

    except Exception as e:
        logger.error(f"Error getting status: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/contacts', methods=['GET'])
def get_contacts():
    """
    Get list of contacts from the device.

    Returns:
        JSON with list of contact names
    """
    try:
        success, contacts, error = cli.get_contacts_list()

        if success:
            return jsonify({
                'success': True,
                'contacts': contacts,
                'count': len(contacts)
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': error or 'Failed to get contacts',
                'contacts': []
            }), 500

    except Exception as e:
        logger.error(f"Error getting contacts: {e}")
        return jsonify({
            'success': False,
            'error': str(e),
            'contacts': []
        }), 500


@api_bp.route('/contacts/cleanup', methods=['POST'])
def cleanup_contacts():
    """
    Clean up inactive contacts.

    JSON body:
        hours (int): Inactivity threshold in hours (optional, default from config)

    Returns:
        JSON with cleanup result
    """
    try:
        data = request.get_json() or {}
        hours = data.get('hours', config.MC_INACTIVE_HOURS)

        if not isinstance(hours, int) or hours < 1:
            return jsonify({
                'success': False,
                'error': 'Invalid hours value (must be positive integer)'
            }), 400

        # Execute cleanup command
        success, message = cli.clean_inactive_contacts(hours)

        if success:
            return jsonify({
                'success': True,
                'message': f'Cleanup completed: {message}',
                'hours': hours
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error cleaning contacts: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/device/info', methods=['GET'])
def get_device_info():
    """
    Get detailed device information.

    Returns:
        JSON with device info
    """
    try:
        success, info = cli.get_device_info()

        if success:
            return jsonify({
                'success': True,
                'info': info
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': info
            }), 500

    except Exception as e:
        logger.error(f"Error getting device info: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


# =============================================================================
# Special Commands
# =============================================================================

# Registry of available special commands
SPECIAL_COMMANDS = {
    'advert': {
        'function': cli.advert,
        'description': 'Send single advertisement (recommended)',
    },
    'floodadv': {
        'function': cli.floodadv,
        'description': 'Flood advertisement (use sparingly!)',
    },
}


@api_bp.route('/device/command', methods=['POST'])
def execute_special_command():
    """
    Execute a special device command.

    JSON body:
        command (str): Command name (required) - one of: advert, floodadv

    Returns:
        JSON with command result
    """
    try:
        data = request.get_json()

        if not data or 'command' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: command'
            }), 400

        command = data['command'].strip().lower()

        if command not in SPECIAL_COMMANDS:
            return jsonify({
                'success': False,
                'error': f'Unknown command: {command}. Available commands: {", ".join(SPECIAL_COMMANDS.keys())}'
            }), 400

        # Execute the command
        cmd_info = SPECIAL_COMMANDS[command]
        success, message = cmd_info['function']()

        if success:
            return jsonify({
                'success': True,
                'command': command,
                'message': message or f'{command} executed successfully'
            }), 200
        else:
            return jsonify({
                'success': False,
                'command': command,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error executing special command: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/device/commands', methods=['GET'])
def list_special_commands():
    """
    List available special commands.

    Returns:
        JSON with list of available commands
    """
    commands = [
        {'name': name, 'description': info['description']}
        for name, info in SPECIAL_COMMANDS.items()
    ]
    return jsonify({
        'success': True,
        'commands': commands
    }), 200


@api_bp.route('/sync', methods=['POST'])
def sync_messages():
    """
    Trigger message sync from device.

    Returns:
        JSON with sync result
    """
    try:
        success, message = cli.recv_messages()

        if success:
            return jsonify({
                'success': True,
                'message': 'Messages synced successfully'
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error syncing messages: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/archives', methods=['GET'])
def get_archives():
    """
    Get list of available message archives.

    Returns:
        JSON with list of archives, each with:
        - date (str): Archive date in YYYY-MM-DD format
        - message_count (int): Number of messages in archive
        - file_size (int): Archive file size in bytes
    """
    try:
        archives = archive_manager.list_archives()

        return jsonify({
            'success': True,
            'archives': archives,
            'count': len(archives)
        }), 200

    except Exception as e:
        logger.error(f"Error listing archives: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/archive/trigger', methods=['POST'])
def trigger_archive():
    """
    Manually trigger message archiving.

    JSON body:
        date (str): Date to archive in YYYY-MM-DD format (optional, defaults to yesterday)

    Returns:
        JSON with archive operation result
    """
    try:
        data = request.get_json() or {}
        archive_date = data.get('date')

        # Validate date format if provided
        if archive_date:
            try:
                datetime.strptime(archive_date, '%Y-%m-%d')
            except ValueError:
                return jsonify({
                    'success': False,
                    'error': f'Invalid date format: {archive_date}. Expected YYYY-MM-DD'
                }), 400

        # Trigger archiving
        result = archive_manager.archive_messages(archive_date)

        if result['success']:
            return jsonify(result), 200
        else:
            return jsonify(result), 500

    except Exception as e:
        logger.error(f"Error triggering archive: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/channels', methods=['GET'])
def get_channels():
    """
    Get list of configured channels (cached for 30s).

    Returns:
        JSON with channels list
    """
    try:
        success, channels = get_channels_cached()

        if success:
            return jsonify({
                'success': True,
                'channels': channels,
                'count': len(channels)
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': 'Failed to retrieve channels'
            }), 500

    except Exception as e:
        logger.error(f"Error getting channels: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/channels', methods=['POST'])
def create_channel():
    """
    Create a new channel with auto-generated key.

    JSON body:
        name (str): Channel name (required)

    Returns:
        JSON with created channel info
    """
    try:
        data = request.get_json()

        if not data or 'name' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: name'
            }), 400

        name = data['name'].strip()
        if not name:
            return jsonify({
                'success': False,
                'error': 'Channel name cannot be empty'
            }), 400

        # Validate name (no special chars that could break CLI)
        if not re.match(r'^[a-zA-Z0-9_\-]+$', name):
            return jsonify({
                'success': False,
                'error': 'Channel name can only contain letters, numbers, _ and -'
            }), 400

        success, message, key = cli.add_channel(name)

        if success:
            invalidate_channels_cache()  # Clear cache to force refresh
            return jsonify({
                'success': True,
                'message': message,
                'channel': {
                    'name': name,
                    'key': key
                }
            }), 201
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error creating channel: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/channels/join', methods=['POST'])
def join_channel():
    """
    Join an existing channel by setting name and key.

    JSON body:
        name (str): Channel name (required)
        key (str): 32-char hex key (optional for channels starting with #)
        index (int): Channel slot (optional, auto-detect if not provided)

    Returns:
        JSON with result
    """
    try:
        data = request.get_json()

        if not data or 'name' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: name'
            }), 400

        name = data['name'].strip()
        key = data.get('key', '').strip().lower() if 'key' in data else None

        # Validate: key is optional for channels starting with #
        if not name.startswith('#') and not key:
            return jsonify({
                'success': False,
                'error': 'Key is required for channels not starting with #'
            }), 400

        # Auto-detect free slot if not provided
        if 'index' in data:
            index = int(data['index'])
        else:
            # Find first free slot (1-7, skip 0 which is Public)
            success_ch, channels = get_channels_cached()
            if not success_ch:
                return jsonify({
                    'success': False,
                    'error': 'Failed to get current channels'
                }), 500

            used_indices = {ch['index'] for ch in channels}
            index = None
            for i in range(1, 8):  # Assume max 8 channels
                if i not in used_indices:
                    index = i
                    break

            if index is None:
                return jsonify({
                    'success': False,
                    'error': 'No free channel slots available'
                }), 400

        success, message = cli.set_channel(index, name, key)

        if success:
            invalidate_channels_cache()  # Clear cache to force refresh
            return jsonify({
                'success': True,
                'message': f'Joined channel "{name}" at slot {index}',
                'channel': {
                    'index': index,
                    'name': name,
                    'key': key if key else 'auto-generated'
                }
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error joining channel: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/channels/<int:index>', methods=['DELETE'])
def delete_channel(index):
    """
    Remove a channel and delete all its messages.

    Args:
        index: Channel index to remove

    Returns:
        JSON with result
    """
    try:
        # First, delete all messages for this channel
        messages_deleted = parser.delete_channel_messages(index)
        if not messages_deleted:
            logger.warning(f"Failed to delete messages for channel {index}, continuing with channel removal")

        # Then remove the channel itself
        success, message = cli.remove_channel(index)

        if success:
            invalidate_channels_cache()  # Clear cache to force refresh
            return jsonify({
                'success': True,
                'message': f'Channel {index} removed and messages deleted'
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error removing channel: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/channels/<int:index>/qr', methods=['GET'])
def get_channel_qr(index):
    """
    Generate QR code for channel sharing.

    Args:
        index: Channel index

    Query params:
        format: 'json' (default) or 'png'

    Returns:
        JSON with QR data or PNG image
    """
    try:
        import qrcode

        # Get channel info
        success, channels = cli.get_channels()
        if not success:
            return jsonify({
                'success': False,
                'error': 'Failed to get channels'
            }), 500

        channel = next((ch for ch in channels if ch['index'] == index), None)
        if not channel:
            return jsonify({
                'success': False,
                'error': f'Channel {index} not found'
            }), 404

        # Create QR data
        qr_data = {
            'type': 'meshcore_channel',
            'name': channel['name'],
            'key': channel['key']
        }
        qr_json = json.dumps(qr_data)

        format_type = request.args.get('format', 'json')

        if format_type == 'png':
            # Generate PNG QR code
            qr = qrcode.QRCode(
                version=1,
                error_correction=qrcode.constants.ERROR_CORRECT_L,
                box_size=10,
                border=4,
            )
            qr.add_data(qr_json)
            qr.make(fit=True)

            img = qr.make_image(fill_color="black", back_color="white")

            # Convert to PNG bytes
            buf = BytesIO()
            img.save(buf, format='PNG')
            buf.seek(0)

            return send_file(buf, mimetype='image/png')

        else:  # JSON format
            # Generate base64 data URL for inline display
            qr = qrcode.QRCode(version=1, box_size=10, border=4)
            qr.add_data(qr_json)
            qr.make(fit=True)

            img = qr.make_image(fill_color="black", back_color="white")
            buf = BytesIO()
            img.save(buf, format='PNG')
            buf.seek(0)

            img_base64 = base64.b64encode(buf.read()).decode()
            data_url = f"data:image/png;base64,{img_base64}"

            return jsonify({
                'success': True,
                'qr_data': qr_data,
                'qr_image': data_url,
                'qr_text': qr_json
            }), 200

    except ImportError:
        return jsonify({
            'success': False,
            'error': 'QR code library not available'
        }), 500

    except Exception as e:
        logger.error(f"Error generating QR code: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/messages/updates', methods=['GET'])
def get_messages_updates():
    """
    Check for new messages across all channels without fetching full message content.
    Used for intelligent refresh mechanism and unread notifications.

    Query parameters:
        last_seen (str): JSON object with last seen timestamps per channel
                        Format: {"0": 1234567890, "1": 1234567891, ...}

    Returns:
        JSON with update information per channel:
        {
            "success": true,
            "channels": [
                {
                    "index": 0,
                    "name": "Public",
                    "has_updates": true,
                    "latest_timestamp": 1234567900,
                    "unread_count": 5
                },
                ...
            ],
            "total_unread": 10
        }
    """
    try:
        # Parse last_seen timestamps from query param
        last_seen_str = request.args.get('last_seen', '{}')
        try:
            last_seen = json.loads(last_seen_str)
            # Convert keys to integers and values to floats
            last_seen = {int(k): float(v) for k, v in last_seen.items()}
        except (json.JSONDecodeError, ValueError):
            last_seen = {}

        # Get list of channels (cached)
        success_ch, channels = get_channels_cached()
        if not success_ch:
            return jsonify({
                'success': False,
                'error': 'Failed to get channels'
            }), 500

        updates = []
        total_unread = 0

        # Check each channel for new messages
        for channel in channels:
            channel_idx = channel['index']

            # Get latest message for this channel
            messages = parser.read_messages(
                limit=1,
                channel_idx=channel_idx,
                days=7  # Only check recent messages
            )

            latest_timestamp = 0
            if messages and len(messages) > 0:
                latest_timestamp = messages[0]['timestamp']

            # Check if there are updates
            last_seen_ts = last_seen.get(channel_idx, 0)
            has_updates = latest_timestamp > last_seen_ts

            # Count unread messages (messages newer than last_seen)
            unread_count = 0
            if has_updates:
                all_messages = parser.read_messages(
                    limit=500,
                    channel_idx=channel_idx,
                    days=7
                )
                unread_count = sum(1 for msg in all_messages if msg['timestamp'] > last_seen_ts)
                total_unread += unread_count

            updates.append({
                'index': channel_idx,
                'name': channel['name'],
                'has_updates': has_updates,
                'latest_timestamp': latest_timestamp,
                'unread_count': unread_count
            })

        return jsonify({
            'success': True,
            'channels': updates,
            'total_unread': total_unread
        }), 200

    except Exception as e:
        logger.error(f"Error checking message updates: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


# =============================================================================
# Direct Messages (DM) Endpoints
# =============================================================================

@api_bp.route('/dm/conversations', methods=['GET'])
def get_dm_conversations():
    """
    Get list of DM conversations.

    Query params:
        days (int): Filter to last N days (default: 7)

    Returns:
        JSON with conversations list:
        {
            "success": true,
            "conversations": [
                {
                    "conversation_id": "pk_4563b1621b58",
                    "display_name": "daniel5120",
                    "pubkey_prefix": "4563b1621b58",
                    "last_message_timestamp": 1766491173,
                    "last_message_preview": "Hello there...",
                    "unread_count": 0,
                    "message_count": 15
                }
            ],
            "count": 5
        }
    """
    try:
        days = request.args.get('days', default=7, type=int)

        conversations = parser.get_dm_conversations(days=days)

        return jsonify({
            'success': True,
            'conversations': conversations,
            'count': len(conversations)
        }), 200

    except Exception as e:
        logger.error(f"Error getting DM conversations: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/dm/messages', methods=['GET'])
def get_dm_messages():
    """
    Get DM messages for a specific conversation.

    Query params:
        conversation_id (str): Required - conversation identifier (pk_xxx or name_xxx)
        limit (int): Max messages to return (default: 100)
        days (int): Filter to last N days (default: 7)

    Returns:
        JSON with messages list:
        {
            "success": true,
            "conversation_id": "pk_4563b1621b58",
            "display_name": "daniel5120",
            "messages": [...],
            "count": 25
        }
    """
    try:
        conversation_id = request.args.get('conversation_id', type=str)
        if not conversation_id:
            return jsonify({
                'success': False,
                'error': 'Missing required parameter: conversation_id'
            }), 400

        limit = request.args.get('limit', default=100, type=int)
        days = request.args.get('days', default=7, type=int)

        messages, pubkey_to_name = parser.read_dm_messages(
            limit=limit,
            conversation_id=conversation_id,
            days=days
        )

        # Determine display name from conversation_id or messages
        display_name = 'Unknown'
        if conversation_id.startswith('pk_'):
            pk = conversation_id[3:]
            display_name = pubkey_to_name.get(pk, pk[:8] + '...')
        elif conversation_id.startswith('name_'):
            display_name = conversation_id[5:]

        # Also check messages for better name
        for msg in messages:
            if msg['direction'] == 'incoming' and msg.get('sender'):
                display_name = msg['sender']
                break
            elif msg['direction'] == 'outgoing' and msg.get('recipient'):
                display_name = msg['recipient']

        return jsonify({
            'success': True,
            'conversation_id': conversation_id,
            'display_name': display_name,
            'messages': messages,
            'count': len(messages)
        }), 200

    except Exception as e:
        logger.error(f"Error getting DM messages: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/dm/messages', methods=['POST'])
def send_dm_message():
    """
    Send a direct message.

    JSON body:
        recipient (str): Contact name (required)
        text (str): Message content (required)

    Returns:
        JSON with send result:
        {
            "success": true,
            "message": "DM sent",
            "recipient": "daniel5120",
            "status": "pending"
        }
    """
    try:
        data = request.get_json()

        if not data:
            return jsonify({
                'success': False,
                'error': 'Missing JSON body'
            }), 400

        recipient = data.get('recipient', '').strip()
        text = data.get('text', '').strip()

        if not recipient:
            return jsonify({
                'success': False,
                'error': 'Missing required field: recipient'
            }), 400

        if not text:
            return jsonify({
                'success': False,
                'error': 'Missing required field: text'
            }), 400

        # MeshCore message length limit
        byte_length = len(text.encode('utf-8'))
        if byte_length > 200:
            return jsonify({
                'success': False,
                'error': f'Message too long ({byte_length} bytes). Maximum 200 bytes allowed.'
            }), 400

        # Send via CLI
        success, message = cli.send_dm(recipient, text)

        if success:
            return jsonify({
                'success': True,
                'message': 'DM sent',
                'recipient': recipient,
                'status': 'pending'
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error sending DM: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/dm/updates', methods=['GET'])
def get_dm_updates():
    """
    Check for new DMs across all conversations.
    Used for notification badge updates.

    Query params:
        last_seen (str): JSON object with last seen timestamps per conversation
                        Format: {"pk_xxx": 1234567890, "name_yyy": 1234567891, ...}

    Returns:
        JSON with update information:
        {
            "success": true,
            "total_unread": 5,
            "conversations": [
                {
                    "conversation_id": "pk_4563b1621b58",
                    "display_name": "daniel5120",
                    "unread_count": 3,
                    "latest_timestamp": 1766491173
                }
            ]
        }
    """
    try:
        # Parse last_seen timestamps
        last_seen_str = request.args.get('last_seen', '{}')
        try:
            last_seen = json.loads(last_seen_str)
        except json.JSONDecodeError:
            last_seen = {}

        # Get all conversations
        conversations = parser.get_dm_conversations(days=7)

        updates = []
        total_unread = 0

        for conv in conversations:
            conv_id = conv['conversation_id']
            last_seen_ts = last_seen.get(conv_id, 0)

            # Count unread
            if conv['last_message_timestamp'] > last_seen_ts:
                # Need to count actual unread messages
                messages, _ = parser.read_dm_messages(
                    conversation_id=conv_id,
                    days=7
                )
                unread_count = sum(1 for m in messages if m['timestamp'] > last_seen_ts)
            else:
                unread_count = 0

            total_unread += unread_count

            if unread_count > 0:
                updates.append({
                    'conversation_id': conv_id,
                    'display_name': conv['display_name'],
                    'unread_count': unread_count,
                    'latest_timestamp': conv['last_message_timestamp']
                })

        return jsonify({
            'success': True,
            'total_unread': total_unread,
            'conversations': updates
        }), 200

    except Exception as e:
        logger.error(f"Error checking DM updates: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


# =============================================================================
# Contact Management (Existing, Pending Contacts & Settings)
# =============================================================================

@api_bp.route('/contacts/detailed', methods=['GET'])
def get_contacts_detailed_api():
    """
    Get detailed list of ALL existing contacts on the device (CLI, REP, ROOM, SENS).

    Returns full contact_info data from meshcli including GPS coordinates, paths, etc.

    Returns:
        JSON with contacts list:
        {
            "success": true,
            "count": 263,
            "limit": 350,
            "contacts": [
                {
                    "name": "TK Zalesie Test 🦜",              // adv_name
                    "public_key": "df2027d3f2ef...",           // Full public key (64 chars)
                    "public_key_prefix": "df2027d3f2ef",       // First 12 chars
                    "type": 2,                                  // 1=CLI, 2=REP, 3=ROOM, 4=SENS
                    "type_label": "REP",                        // Human-readable type
                    "flags": 0,
                    "out_path_len": -1,                         // -1 = Flood mode
                    "out_path": "",                             // Path string
                    "last_advert": 1735429453,                  // Unix timestamp
                    "adv_lat": 50.866005,                       // GPS latitude
                    "adv_lon": 20.669308,                       // GPS longitude
                    "lastmod": 1715973527                       // Last modification timestamp
                },
                ...
            ]
        }
    """
    try:
        # Get detailed contact info from meshcli (includes all fields)
        success_detailed, contacts_detailed, error_detailed = cli.get_contacts_with_last_seen()

        if not success_detailed:
            return jsonify({
                'success': False,
                'error': error_detailed or 'Failed to get contact details',
                'contacts': [],
                'count': 0,
                'limit': 350
            }), 500

        # Convert dict to list and add computed fields
        type_labels = {1: 'CLI', 2: 'REP', 3: 'ROOM', 4: 'SENS'}
        contacts = []

        for public_key, details in contacts_detailed.items():
            # Compute path display string
            out_path_len = details.get('out_path_len', -1)
            out_path = details.get('out_path', '')
            if out_path_len == -1:
                path_or_mode = 'Flood'
            elif out_path:
                path_or_mode = out_path
            else:
                path_or_mode = f'Path len: {out_path_len}'

            contact = {
                # All original fields from contact_info
                'public_key': public_key,
                'type': details.get('type'),
                'flags': details.get('flags'),
                'out_path_len': out_path_len,
                'out_path': out_path,
                'last_advert': details.get('last_advert'),
                'adv_lat': details.get('adv_lat'),
                'adv_lon': details.get('adv_lon'),
                'lastmod': details.get('lastmod'),
                # Computed/convenience fields
                'name': details.get('adv_name', ''),  # Map adv_name to name for compatibility
                'public_key_prefix': public_key[:12] if len(public_key) >= 12 else public_key,
                'type_label': type_labels.get(details.get('type'), 'UNKNOWN'),
                'path_or_mode': path_or_mode,  # For UI display
                'last_seen': details.get('last_advert'),  # Alias for compatibility
            }
            contacts.append(contact)

        return jsonify({
            'success': True,
            'contacts': contacts,
            'count': len(contacts),
            'limit': 350  # MeshCore device limit
        }), 200

    except Exception as e:
        logger.error(f"Error getting detailed contacts list: {e}")
        return jsonify({
            'success': False,
            'error': str(e),
            'contacts': [],
            'count': 0,
            'limit': 350
        }), 500


@api_bp.route('/contacts/delete', methods=['POST'])
def delete_contact_api():
    """
    Delete a contact from the device.

    JSON body:
        {
            "selector": "<public_key_prefix_or_name>"
        }

    Using public_key_prefix is recommended for reliability.

    Returns:
        JSON with deletion result:
        {
            "success": true,
            "message": "Contact removed successfully"
        }
    """
    try:
        data = request.get_json()

        if not data or 'selector' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: selector'
            }), 400

        selector = data['selector']

        if not isinstance(selector, str) or not selector.strip():
            return jsonify({
                'success': False,
                'error': 'selector must be a non-empty string'
            }), 400

        success, message = cli.delete_contact(selector)

        if success:
            return jsonify({
                'success': True,
                'message': message
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error deleting contact: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


# =============================================================================
# Contact Management (Pending Contacts & Settings)
# =============================================================================

@api_bp.route('/contacts/pending', methods=['GET'])
def get_pending_contacts_api():
    """
    Get list of contacts awaiting manual approval.

    Returns:
        JSON with pending contacts list:
        {
            "success": true,
            "pending": [
                {
                    "name": "Skyllancer",
                    "public_key": "f9ef123abc..."
                },
                ...
            ],
            "count": 2
        }
    """
    try:
        success, pending, error = cli.get_pending_contacts()

        if success:
            return jsonify({
                'success': True,
                'pending': pending,
                'count': len(pending)
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': error or 'Failed to get pending contacts',
                'pending': []
            }), 500

    except Exception as e:
        logger.error(f"Error getting pending contacts: {e}")
        return jsonify({
            'success': False,
            'error': str(e),
            'pending': []
        }), 500


@api_bp.route('/contacts/pending/approve', methods=['POST'])
def approve_pending_contact_api():
    """
    Approve and add a pending contact.

    JSON body:
        {
            "public_key": "<full_public_key>"
        }

    IMPORTANT: Always send the full public_key (not name or prefix).
    Full public key works for all contact types (CLI, ROOM, REP, SENS).

    Returns:
        JSON with approval result:
        {
            "success": true,
            "message": "Contact approved successfully"
        }
    """
    try:
        data = request.get_json()

        if not data or 'public_key' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: public_key'
            }), 400

        public_key = data['public_key']

        if not isinstance(public_key, str) or not public_key.strip():
            return jsonify({
                'success': False,
                'error': 'public_key must be a non-empty string'
            }), 400

        success, message = cli.approve_pending_contact(public_key)

        if success:
            return jsonify({
                'success': True,
                'message': message or 'Contact approved successfully'
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error approving pending contact: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@api_bp.route('/device/settings', methods=['GET'])
def get_device_settings_api():
    """
    Get persistent device settings.

    Returns:
        JSON with settings:
        {
            "success": true,
            "settings": {
                "manual_add_contacts": false
            }
        }
    """
    try:
        success, settings = cli.get_device_settings()

        if success:
            return jsonify({
                'success': True,
                'settings': settings
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': 'Failed to get device settings',
                'settings': {'manual_add_contacts': False}
            }), 500

    except Exception as e:
        logger.error(f"Error getting device settings: {e}")
        return jsonify({
            'success': False,
            'error': str(e),
            'settings': {'manual_add_contacts': False}
        }), 500


@api_bp.route('/device/settings', methods=['POST'])
def update_device_settings_api():
    """
    Update persistent device settings.

    JSON body:
        {
            "manual_add_contacts": true/false
        }

    This setting is:
    1. Saved to .webui_settings.json for persistence across container restarts
    2. Applied immediately to the running meshcli session

    Returns:
        JSON with update result:
        {
            "success": true,
            "message": "manual_add_contacts set to on"
        }
    """
    try:
        data = request.get_json()

        if not data or 'manual_add_contacts' not in data:
            return jsonify({
                'success': False,
                'error': 'Missing required field: manual_add_contacts'
            }), 400

        manual_add_contacts = data['manual_add_contacts']

        if not isinstance(manual_add_contacts, bool):
            return jsonify({
                'success': False,
                'error': 'manual_add_contacts must be a boolean'
            }), 400

        success, message = cli.set_manual_add_contacts(manual_add_contacts)

        if success:
            return jsonify({
                'success': True,
                'message': message,
                'settings': {'manual_add_contacts': manual_add_contacts}
            }), 200
        else:
            return jsonify({
                'success': False,
                'error': message
            }), 500

    except Exception as e:
        logger.error(f"Error updating device settings: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500
