import 'dart:io' show Platform;

import 'package:flutter/services.dart';

import '../src/rust/api.dart' as rust;

const iosAudioLifecycleChannelName = 'chanora/ios_audio_lifecycle';
const androidAudioLifecycleChannelName = 'chanora/android_audio_lifecycle';
const macosAudioLifecycleChannelName = 'chanora/macos_audio_lifecycle';

/// Parses a platform-channel route string into a [rust.BridgeAudioRoute].
///
/// The producer contract is:
/// - iOS: `AppDelegate.classifyAudioRoute(_:)` emits one of
///   `Earpiece`, `Speaker`, `WiredHeadset`, `BluetoothHfp`, `BluetoothA2dp`,
///   `Unknown`.
/// - Android: `AndroidAudioLifecycleController.classifyDevice` emits one of
///   `Earpiece`, `Speaker`, `WiredHeadset`, `UsbHeadset`, `BluetoothHfp`,
///   `BluetoothA2dp`, `Hdmi`, `Unknown`.
///
/// Both producers emit exact PascalCase strings. Case variants (`USB_HEADSET`,
/// `usb_headset`, `UsbHeadphone`) are NOT handled and will fall through to
/// `unknown`. If either platform classifier changes its string contract,
/// update both producers and this parser together.
rust.BridgeAudioRoute parseBridgeAudioRoute(String value) {
  switch (value) {
    case 'Earpiece':
      return rust.BridgeAudioRoute.earpiece;
    case 'Speaker':
      return rust.BridgeAudioRoute.speaker;
    case 'WiredHeadset':
    case 'UsbHeadset':
      return rust.BridgeAudioRoute.wiredHeadset;
    case 'BluetoothHfp':
      return rust.BridgeAudioRoute.bluetoothHfp;
    case 'BluetoothA2dp':
      return rust.BridgeAudioRoute.bluetoothA2Dp;
    case 'Hdmi':
    case 'Unknown':
      return rust.BridgeAudioRoute.unknown;
    default:
      return rust.BridgeAudioRoute.unknown;
  }
}

void wireAudioLifecycle() {
  wireIosAudioLifecycle();
  wireAndroidAudioLifecycle();
  wireMacosAudioLifecycle();
}

/// Wire the iOS AVAudioSession lifecycle MethodChannel.
///
/// Swift side (AppDelegate) posts route-change and interruption events through
/// this channel. The handler dispatches them to the FRB bridge functions on
/// the Rust side.
void wireIosAudioLifecycle({
  MethodChannel channel = const MethodChannel(iosAudioLifecycleChannelName),
}) {
  channel.setMethodCallHandler((call) async {
    try {
      switch (call.method) {
        case 'handleRouteChange':
          final routeStr = call.arguments as String? ?? 'Unknown';
          final route = parseBridgeAudioRoute(routeStr);
          rust.handleRouteChange(route: route);
          break;
        case 'handleMediaServicesReset':
          final routeClass = call.arguments as String? ?? 'Unknown';
          rust.handleMediaServicesResetWithRoute(routeClass: routeClass);
          break;
        case 'handleInterruptionBegan':
          rust.handleInterruptionBegan();
          break;
        case 'handleInterruptionEnded':
          final shouldResume = call.arguments as bool? ?? false;
          rust.handleInterruptionEnded(shouldResume: shouldResume);
          break;
        case 'handleWillResignActive':
        case 'handleDidEnterBackground':
          rust.handleInterruptionBegan();
          break;
        case 'handleWillEnterForeground':
          rust.handleInterruptionEnded(shouldResume: true);
          break;
        case 'handleWillTerminate':
          rust.handleInterruptionBegan();
          break;
        default:
          break;
      }
    } catch (_) {
      // Errors from the Rust side are already logged there; do not propagate
      // exceptions to the platform framework.
    }
  });
}

/// Wire the Android audio lifecycle MethodChannel.
///
/// Kotlin side (`AndroidAudioLifecycleController`) posts route-change events
/// through this channel. This mirrors the iOS dispatch path.
void wireAndroidAudioLifecycle({
  bool isAndroid = false,
  MethodChannel channel = const MethodChannel(androidAudioLifecycleChannelName),
}) {
  if (!isAndroid && !Platform.isAndroid) return;
  channel.setMethodCallHandler((call) async {
    try {
      switch (call.method) {
        case 'handleRouteChange':
          final args = call.arguments;
          final routeStr = args is Map
              ? (args['routeType'] as String? ?? 'Unknown')
              : (args as String? ?? 'Unknown');
          final route = parseBridgeAudioRoute(routeStr);
          rust.handleRouteChange(route: route);
          break;
        default:
          break;
      }
    } catch (_) {
      // Errors from the Rust side are already logged there; do not propagate
      // exceptions to the platform framework.
    }
  });
}

/// Wire the macOS audio lifecycle MethodChannel.
///
/// Swift side (`MacOSAudioLifecycle`) posts `handleDefaultDeviceChange`
/// (with `role: 'input' | 'output'`) when Core Audio HAL default-input /
/// default-output device changes, and `handleConfigurationChange` when
/// the VPIO AudioUnit reports a stream-format change. Closes the
/// iOS/macOS asymmetry noted in SysRS-051.
///
/// Current scope: events are received and logged. The FRB
/// `macosDefaultDeviceChanged` function that triggers a VPIO
/// re-bind on the engine is a follow-up; until it's exposed, the
/// macOS path mirrors the iOS `chanora/ios_audio_lifecycle` event
/// surface but does not yet trigger an engine-side restart.
void wireMacosAudioLifecycle({
  bool isMacos = false,
  MethodChannel channel = const MethodChannel(macosAudioLifecycleChannelName),
}) {
  if (!isMacos && !Platform.isMacOS) return;
  channel.setMethodCallHandler((call) async {
    try {
      switch (call.method) {
        case 'handleDefaultDeviceChange':
          // TODO: call rust.macosDefaultDeviceChanged() once exposed
          // via flutter_rust_bridge; until then the event is captured
          // here for observability.
          break;
        case 'handleConfigurationChange':
          // TODO: same — currently captured, no engine action yet.
          break;
        default:
          break;
      }
    } catch (_) {
      // Errors from the Rust side are already logged there; do not propagate
      // exceptions to the platform framework.
    }
  });
}
