"""
API endpoint for frontend status polling
"""
import asyncio
import os
import signal
from typing import List, Optional

from fastapi import APIRouter, Request
from loguru import logger
from pydantic import BaseModel

router = APIRouter()


class BrokerStatus(BaseModel):
    id: str
    name: str
    connected: bool
    accounts: List[str] = []  # Account IDs managed by this broker


class StatusResponse(BaseModel):
    status: str = 'ok'
    version: str
    instanceId: Optional[str] = None
    instanceName: Optional[str] = None  # Display name for the instance
    hubConnected: bool = False
    hubDisconnectReason: Optional[str] = None  # Reason for hub disconnection (e.g., subscription expired)
    brokers: List[BrokerStatus] = []
    connectedAccounts: List[str] = []  # All connected account IDs (for easy filtering)
    # Computed fields for backward compatibility
    brokerConnected: bool = False  # True if ALL brokers connected
    brokerPartial: bool = False    # True if SOME but not all connected
    # OTB-398: Update availability
    updateAvailable: bool = False
    latestVersion: Optional[str] = None


@router.get('/status', response_model=StatusResponse)
async def get_status(request: Request) -> StatusResponse:
    """
    Get the current status of the OptraBot instance.
    Used by the frontend to poll connection status.
    """
    from optrabot.broker.brokerfactory import BrokerFactory
    from optrabot.optrabot import get_version
    
    optrabot = request.app.optraBot
    
    # Check hub connection status and disconnect reason
    hub_connected = False
    hub_disconnect_reason = None
    if hasattr(optrabot, 'thc') and optrabot.thc:
        hub_connected = optrabot.thc.isHubConnectionOK()
        if not hub_connected:
            hub_disconnect_reason = optrabot.thc.getHubDisconnectReason()
    
    # Check broker connection status
    broker_factory = BrokerFactory()
    connectors = broker_factory.get_broker_connectors()
    
    brokers: List[BrokerStatus] = []
    connected_count = 0
    all_connected_accounts: List[str] = []
    
    if connectors:
        for connector_id, connector in connectors.items():
            is_conn = connector.isConnected() or False  # Ensure boolean even if isConnected() returns None
            account_ids: List[str] = []
            
            if is_conn:
                connected_count += 1
                # Get accounts for connected brokers
                try:
                    accounts = connector.getAccounts()
                    account_ids = [acc.id for acc in accounts]
                    all_connected_accounts.extend(account_ids)
                except Exception as e:
                    logger.warning(f'Failed to get accounts for broker {connector_id}: {e}')
            
            brokers.append(BrokerStatus(
                id=connector_id,
                name=type(connector).__name__,
                connected=is_conn,
                accounts=account_ids
            ))
    
    total_brokers = len(brokers)
    all_connected = total_brokers > 0 and connected_count == total_brokers
    partial_connected = connected_count > 0 and connected_count < total_brokers
    
    # Get instance ID and name from config
    instance_id = None
    instance_name = None
    if hasattr(optrabot, 'config') and optrabot.config:
        instance_id = optrabot.config.getInstanceId()
        # Use instance ID as display name if it differs from default
        if instance_id and instance_id.lower() != 'optrabot':
            instance_name = instance_id
    
    return StatusResponse(
        status='ok',
        version=get_version(),
        instanceId=instance_id,
        instanceName=instance_name,
        hubConnected=hub_connected,
        hubDisconnectReason=hub_disconnect_reason,
        brokers=brokers,
        connectedAccounts=all_connected_accounts,
        brokerConnected=all_connected,
        brokerPartial=partial_connected,
        updateAvailable=optrabot._update_available,
        latestVersion=optrabot._latest_version
    )


class CheckUpdateResponse(BaseModel):
    updateAvailable: bool = False
    latestVersion: Optional[str] = None
    currentVersion: str


@router.post('/api/check-update', response_model=CheckUpdateResponse)
async def check_update(request: Request) -> CheckUpdateResponse:
    """
    OTB-398: Manually trigger an update check against PyPI.
    """
    from optrabot.optrabot import get_version

    optrabot = request.app.optraBot
    await optrabot.check_for_update()
    return CheckUpdateResponse(
        updateAvailable=optrabot._update_available,
        latestVersion=optrabot._latest_version,
        currentVersion=get_version()
    )


@router.post('/api/update')
async def trigger_update(request: Request):
    """
    OTB-398: Trigger an update installation. The server will shut down,
    update the package, and restart with --no-browser so the existing
    browser tab can reconnect automatically.
    """
    from optrabot.main import perform_update

    optrabot = request.app.optraBot
    if not optrabot._update_available:
        return {"success": False, "message": "No update available"}

    # Perform update in a delayed task so the HTTP response is sent first
    async def delayed_update():
        await asyncio.sleep(1)
        try:
            perform_update()
        except SystemExit:
            # perform_update() calls exit(0) after launching the update script.
            # Inside asyncio this raises SystemExit instead of terminating.
            os._exit(0)

    asyncio.create_task(delayed_update())
    return {"success": True, "message": "Update started. OptraBot will restart shortly."}
