from __future__ import annotations

from datetime import UTC, datetime, timedelta
from typing import TYPE_CHECKING, Any

from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.crud.server_squad import (
    count_active_users_for_squad,
    get_server_squad_by_uuid,
)

from ..dependencies import get_db_session, require_api_token
from ..schemas.remnawave import (
    RemnaWaveConnectionStatus,
    RemnaWaveGenericSyncResponse,
    RemnaWaveInboundsResponse,
    RemnaWaveNode,
    RemnaWaveNodeActionRequest,
    RemnaWaveNodeActionResponse,
    RemnaWaveNodeListResponse,
    RemnaWaveNodeStatisticsResponse,
    RemnaWaveNodeUsageResponse,
    RemnaWaveOperationResponse,
    RemnaWaveSquad,
    RemnaWaveSquadActionRequest,
    RemnaWaveSquadCreateRequest,
    RemnaWaveSquadListResponse,
    RemnaWaveSquadMigrationPreviewResponse,
    RemnaWaveSquadMigrationRequest,
    RemnaWaveSquadMigrationResponse,
    RemnaWaveSquadMigrationStats,
    RemnaWaveSquadUpdateRequest,
    RemnaWaveStatusResponse,
    RemnaWaveSyncFromPanelRequest,
    RemnaWaveSystemStatsResponse,
    RemnaWaveUserTrafficResponse,
)


try:  # pragma: no cover - импорт может не работать без optional-зависимостей
    from app.services.remnawave_service import (  # type: ignore
        RemnaWaveConfigurationError,
        RemnaWaveService,
    )
except Exception:  # pragma: no cover - при ошибке импорта скрываем функционал
    RemnaWaveConfigurationError = None  # type: ignore[assignment]
    RemnaWaveService = None  # type: ignore[assignment]

if TYPE_CHECKING:  # pragma: no cover - только для типов в IDE
    from app.services.remnawave_service import RemnaWaveService as RemnaWaveServiceType
else:
    RemnaWaveServiceType = Any


router = APIRouter()


def _get_service() -> RemnaWaveServiceType:
    if RemnaWaveService is None:  # pragma: no cover - зависимость не доступна
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail='RemnaWave сервис недоступен',
        )

    return RemnaWaveService()


def _ensure_service_configured(service: RemnaWaveServiceType) -> None:
    if RemnaWaveService is None:  # pragma: no cover - зависимость не доступна
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail='RemnaWave сервис недоступен',
        )

    if not service.is_configured:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=service.configuration_error or 'RemnaWave API не настроен',
        )


def _serialize_node(node_data: dict[str, Any]) -> RemnaWaveNode:
    return RemnaWaveNode(
        uuid=node_data.get('uuid', ''),
        name=node_data.get('name', ''),
        address=node_data.get('address', ''),
        country_code=node_data.get('country_code'),
        is_connected=bool(node_data.get('is_connected')),
        is_disabled=bool(node_data.get('is_disabled')),
        is_node_online=bool(node_data.get('is_node_online')),
        is_xray_running=bool(node_data.get('is_xray_running')),
        users_online=node_data.get('users_online'),
        traffic_used_bytes=node_data.get('traffic_used_bytes'),
        traffic_limit_bytes=node_data.get('traffic_limit_bytes'),
        last_status_change=_parse_last_updated(node_data.get('last_status_change')),
        last_status_message=node_data.get('last_status_message'),
        xray_uptime=node_data.get('xray_uptime'),
        is_traffic_tracking_active=bool(node_data.get('is_traffic_tracking_active', False)),
        traffic_reset_day=node_data.get('traffic_reset_day'),
        notify_percent=node_data.get('notify_percent'),
        consumption_multiplier=float(node_data.get('consumption_multiplier', 1.0)),
        cpu_count=node_data.get('cpu_count'),
        cpu_model=node_data.get('cpu_model'),
        total_ram=node_data.get('total_ram'),
        created_at=_parse_last_updated(node_data.get('created_at')),
        updated_at=_parse_last_updated(node_data.get('updated_at')),
        provider_uuid=node_data.get('provider_uuid'),
    )


def _parse_last_updated(value: Any) -> datetime | None:
    if isinstance(value, datetime):
        return value
    if isinstance(value, str):
        try:
            parsed = datetime.fromisoformat(value)
            if parsed.tzinfo is None:
                return parsed.replace(tzinfo=UTC)
            return parsed
        except ValueError:
            return None
    return None


@router.get('/status', response_model=RemnaWaveStatusResponse)
async def get_remnawave_status(
    _: Any = Security(require_api_token),
) -> RemnaWaveStatusResponse:
    service = _get_service()

    connection_info: RemnaWaveConnectionStatus | None = None
    connection_result = await service.test_api_connection()

    if connection_result:
        connection_info = RemnaWaveConnectionStatus(**connection_result)

    return RemnaWaveStatusResponse(
        is_configured=service.is_configured,
        configuration_error=service.configuration_error,
        connection=connection_info,
    )


@router.get('/system', response_model=RemnaWaveSystemStatsResponse)
async def get_system_statistics(
    _: Any = Security(require_api_token),
) -> RemnaWaveSystemStatsResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.get_system_statistics()
    if not stats or 'system' not in stats:
        raise HTTPException(status.HTTP_502_BAD_GATEWAY, 'Не удалось получить статистику RemnaWave')

    stats['last_updated'] = _parse_last_updated(stats.get('last_updated'))
    return RemnaWaveSystemStatsResponse(**stats)


@router.get('/nodes', response_model=RemnaWaveNodeListResponse)
async def list_nodes(
    _: Any = Security(require_api_token),
) -> RemnaWaveNodeListResponse:
    service = _get_service()
    _ensure_service_configured(service)

    nodes = await service.get_all_nodes()
    serialized = [_serialize_node(node) for node in nodes]
    return RemnaWaveNodeListResponse(items=serialized, total=len(serialized))


@router.get('/nodes/realtime', response_model=list[dict[str, Any]])
async def get_nodes_realtime_usage(
    _: Any = Security(require_api_token),
) -> list[dict[str, Any]]:
    service = _get_service()
    _ensure_service_configured(service)
    return await service.get_nodes_realtime_usage()


@router.get('/nodes/{node_uuid}', response_model=RemnaWaveNode)
async def get_node_details(
    node_uuid: str,
    _: Any = Security(require_api_token),
) -> RemnaWaveNode:
    service = _get_service()
    _ensure_service_configured(service)

    node = await service.get_node_details(node_uuid)
    if not node:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Нода не найдена')
    return _serialize_node(node)


@router.get('/nodes/{node_uuid}/statistics', response_model=RemnaWaveNodeStatisticsResponse)
async def get_node_statistics(
    node_uuid: str,
    _: Any = Security(require_api_token),
) -> RemnaWaveNodeStatisticsResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.get_node_statistics(node_uuid)
    if not stats or not stats.get('node'):
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Не удалось получить информацию по ноде')

    node_data = _serialize_node(stats['node'])
    usage_history = stats.get('usage_history') or []
    realtime = stats.get('realtime')
    last_updated = _parse_last_updated(stats.get('last_updated'))

    return RemnaWaveNodeStatisticsResponse(
        node=node_data,
        realtime=realtime,
        usage_history=usage_history,
        last_updated=last_updated,
    )


@router.get('/nodes/{node_uuid}/usage', response_model=RemnaWaveNodeUsageResponse)
async def get_node_usage_range(
    node_uuid: str,
    start: datetime | None = Query(default=None),
    end: datetime | None = Query(default=None),
    _: Any = Security(require_api_token),
) -> RemnaWaveNodeUsageResponse:
    service = _get_service()
    _ensure_service_configured(service)

    end_dt = end or datetime.now(UTC)
    start_dt = start or (end_dt - timedelta(days=7))

    if start_dt >= end_dt:
        raise HTTPException(status.HTTP_400_BAD_REQUEST, 'Некорректный диапазон дат')

    usage = await service.get_node_user_usage_by_range(node_uuid, start_dt, end_dt)
    return RemnaWaveNodeUsageResponse(items=usage or [])


@router.post('/nodes/{node_uuid}/actions', response_model=RemnaWaveNodeActionResponse)
async def manage_node(
    node_uuid: str,
    payload: RemnaWaveNodeActionRequest,
    _: Any = Security(require_api_token),
) -> RemnaWaveNodeActionResponse:
    service = _get_service()
    _ensure_service_configured(service)

    success = await service.manage_node(node_uuid, payload.action)
    detail = None
    if success:
        if payload.action == 'enable':
            detail = 'Нода включена'
        elif payload.action == 'disable':
            detail = 'Нода отключена'
        elif payload.action == 'restart':
            detail = 'Команда перезапуска отправлена'
    else:
        detail = 'Не удалось выполнить действие'

    return RemnaWaveNodeActionResponse(success=success, detail=detail)


@router.post('/nodes/restart', response_model=RemnaWaveNodeActionResponse)
async def restart_all_nodes(
    _: Any = Security(require_api_token),
) -> RemnaWaveNodeActionResponse:
    service = _get_service()
    _ensure_service_configured(service)

    success = await service.restart_all_nodes()
    detail = 'Команда перезапуска отправлена' if success else 'Не удалось перезапустить ноды'
    return RemnaWaveNodeActionResponse(success=success, detail=detail)


@router.get('/squads', response_model=RemnaWaveSquadListResponse)
async def list_squads(
    _: Any = Security(require_api_token),
) -> RemnaWaveSquadListResponse:
    service = _get_service()
    _ensure_service_configured(service)

    squads = await service.get_all_squads()
    serialized = [RemnaWaveSquad(**squad) for squad in squads]
    return RemnaWaveSquadListResponse(items=serialized, total=len(serialized))


@router.get('/squads/{squad_uuid}', response_model=RemnaWaveSquad)
async def get_squad_details(
    squad_uuid: str,
    _: Any = Security(require_api_token),
) -> RemnaWaveSquad:
    service = _get_service()
    _ensure_service_configured(service)

    squad = await service.get_squad_details(squad_uuid)
    if not squad:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Сквад не найден')
    return RemnaWaveSquad(**squad)


@router.post('/squads', response_model=RemnaWaveOperationResponse, status_code=status.HTTP_201_CREATED)
async def create_squad(
    payload: RemnaWaveSquadCreateRequest,
    _: Any = Security(require_api_token),
) -> RemnaWaveOperationResponse:
    service = _get_service()
    _ensure_service_configured(service)

    squad_uuid = await service.create_squad(payload.name, payload.inbound_uuids)

    success = squad_uuid is not None
    detail = 'Сквад успешно создан' if success else 'Не удалось создать сквад'
    data = {'uuid': squad_uuid} if success else None

    return RemnaWaveOperationResponse(success=success, detail=detail, data=data)


@router.patch('/squads/{squad_uuid}', response_model=RemnaWaveOperationResponse)
async def update_squad(
    squad_uuid: str,
    payload: RemnaWaveSquadUpdateRequest,
    _: Any = Security(require_api_token),
) -> RemnaWaveOperationResponse:
    service = _get_service()
    _ensure_service_configured(service)

    success = False
    detail = 'Необходимо указать новые данные'

    if payload.name is not None or payload.inbound_uuids is not None:
        success = await service.update_squad(
            squad_uuid,
            name=payload.name,
            inbounds=payload.inbound_uuids,
        )
        detail = 'Сквад обновлен' if success else 'Не удалось обновить сквад'

    return RemnaWaveOperationResponse(success=success, detail=detail)


@router.post('/squads/{squad_uuid}/actions', response_model=RemnaWaveOperationResponse)
async def squad_actions(
    squad_uuid: str,
    payload: RemnaWaveSquadActionRequest,
    _: Any = Security(require_api_token),
) -> RemnaWaveOperationResponse:
    service = _get_service()
    _ensure_service_configured(service)

    action = payload.action
    success = False
    detail = 'Неизвестное действие'

    if action == 'add_all_users':
        success = await service.add_all_users_to_squad(squad_uuid)
        detail = 'Пользователи добавлены' if success else 'Не удалось добавить пользователей'
    elif action == 'remove_all_users':
        success = await service.remove_all_users_from_squad(squad_uuid)
        detail = 'Пользователи удалены' if success else 'Не удалось удалить пользователей'
    elif action == 'delete':
        success = await service.delete_squad(squad_uuid)
        detail = 'Сквад удален' if success else 'Не удалось удалить сквад'
    elif action == 'rename':
        if not payload.name:
            raise HTTPException(status.HTTP_400_BAD_REQUEST, 'Необходимо указать новое имя')
        success = await service.rename_squad(squad_uuid, payload.name)
        detail = 'Сквад переименован' if success else 'Не удалось переименовать сквад'
    elif action == 'update_inbounds':
        if not payload.inbound_uuids:
            raise HTTPException(status.HTTP_400_BAD_REQUEST, 'Необходимо указать inbound_uuids')
        success = await service.update_squad_inbounds(squad_uuid, payload.inbound_uuids)
        detail = 'Инбаунды обновлены' if success else 'Не удалось обновить инбаунды'

    return RemnaWaveOperationResponse(success=success, detail=detail)


@router.get('/inbounds', response_model=RemnaWaveInboundsResponse)
async def list_inbounds(
    _: Any = Security(require_api_token),
) -> RemnaWaveInboundsResponse:
    service = _get_service()
    _ensure_service_configured(service)

    inbounds = await service.get_all_inbounds()
    return RemnaWaveInboundsResponse(items=inbounds or [])


@router.get('/users/{telegram_id}/traffic', response_model=RemnaWaveUserTrafficResponse)
async def get_user_traffic(
    telegram_id: int,
    _: Any = Security(require_api_token),
) -> RemnaWaveUserTrafficResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.get_user_traffic_stats(telegram_id)
    if not stats:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Пользователь не найден в RemnaWave')

    return RemnaWaveUserTrafficResponse(telegram_id=telegram_id, **stats)


@router.get('/squads/{squad_uuid}/migration-preview', response_model=RemnaWaveSquadMigrationPreviewResponse)
async def preview_squad_migration(
    squad_uuid: str,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveSquadMigrationPreviewResponse:
    service = _get_service()
    _ensure_service_configured(service)

    squad = await get_server_squad_by_uuid(db, squad_uuid)
    if not squad:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Сквад не найден')

    users_to_migrate = await count_active_users_for_squad(db, squad_uuid)

    return RemnaWaveSquadMigrationPreviewResponse(
        squad_uuid=squad.squad_uuid,
        squad_name=squad.display_name,
        current_users=squad.current_users or 0,
        max_users=squad.max_users,
        users_to_migrate=users_to_migrate,
    )


@router.post('/sync/from-panel', response_model=RemnaWaveGenericSyncResponse)
async def sync_from_panel(
    payload: RemnaWaveSyncFromPanelRequest,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveGenericSyncResponse:
    service = _get_service()
    _ensure_service_configured(service)

    try:
        stats = await service.sync_users_from_panel(db, payload.mode)
        detail = 'Синхронизация из панели выполнена'
        return RemnaWaveGenericSyncResponse(success=True, detail=detail, data=stats)
    except Exception as exc:  # pragma: no cover - точный тип зависит от импорта
        if RemnaWaveConfigurationError and isinstance(exc, RemnaWaveConfigurationError):
            raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, str(exc)) from exc
        raise


@router.post('/sync/to-panel', response_model=RemnaWaveGenericSyncResponse)
async def sync_to_panel(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveGenericSyncResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.sync_users_to_panel(db)
    detail = 'Синхронизация в панель выполнена'
    return RemnaWaveGenericSyncResponse(success=True, detail=detail, data=stats)


@router.post('/sync/subscriptions/validate', response_model=RemnaWaveGenericSyncResponse)
async def validate_and_fix_subscriptions(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveGenericSyncResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.validate_and_fix_subscriptions(db)
    detail = 'Подписки проверены'
    return RemnaWaveGenericSyncResponse(success=True, detail=detail, data=stats)


@router.post('/sync/subscriptions/cleanup', response_model=RemnaWaveGenericSyncResponse)
async def cleanup_orphaned_subscriptions(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveGenericSyncResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.cleanup_orphaned_subscriptions(db)
    detail = 'Очистка завершена'
    return RemnaWaveGenericSyncResponse(success=True, detail=detail, data=stats)


@router.post('/sync/subscriptions/statuses', response_model=RemnaWaveGenericSyncResponse)
async def sync_subscription_statuses(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveGenericSyncResponse:
    service = _get_service()
    _ensure_service_configured(service)

    stats = await service.sync_subscription_statuses(db)
    detail = 'Статусы подписок синхронизированы'
    return RemnaWaveGenericSyncResponse(success=True, detail=detail, data=stats)


@router.get('/sync/recommendations', response_model=RemnaWaveGenericSyncResponse)
async def get_sync_recommendations(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveGenericSyncResponse:
    service = _get_service()
    _ensure_service_configured(service)

    data = await service.get_sync_recommendations(db)
    detail = 'Рекомендации получены'
    return RemnaWaveGenericSyncResponse(success=True, detail=detail, data=data)


@router.post('/squads/migrate', response_model=RemnaWaveSquadMigrationResponse)
async def migrate_squad(
    payload: RemnaWaveSquadMigrationRequest,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> RemnaWaveSquadMigrationResponse:
    service = _get_service()
    _ensure_service_configured(service)

    source_uuid = payload.source_uuid.strip()
    target_uuid = payload.target_uuid.strip()

    if source_uuid == target_uuid:
        raise HTTPException(status.HTTP_400_BAD_REQUEST, 'Источник и назначение совпадают')

    source = await get_server_squad_by_uuid(db, source_uuid)
    if not source:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Сквад-источник не найден')

    target = await get_server_squad_by_uuid(db, target_uuid)
    if not target:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Сквад-назначение не найден')

    try:
        result = await service.migrate_squad_users(
            db,
            source_uuid=source.squad_uuid,
            target_uuid=target.squad_uuid,
        )
    except RemnaWaveConfigurationError as exc:  # pragma: no cover - зависит от окружения
        raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, str(exc)) from exc

    if not result.get('success'):
        detail = result.get('message') or 'Не удалось выполнить переезд'
        return RemnaWaveSquadMigrationResponse(
            success=False,
            detail=detail,
            error=result.get('error'),
        )

    stats = RemnaWaveSquadMigrationStats(
        source_uuid=source.squad_uuid,
        target_uuid=target.squad_uuid,
        total=result.get('total', 0),
        updated=result.get('updated', 0),
        panel_updated=result.get('panel_updated', 0),
        panel_failed=result.get('panel_failed', 0),
        source_removed=result.get('source_removed', 0),
        target_added=result.get('target_added', 0),
    )

    detail = result.get('message') or 'Переезд выполнен'
    return RemnaWaveSquadMigrationResponse(success=True, detail=detail, data=stats)
