import 'dart:async' show Completer;

import 'package:flutter/foundation.dart' show debugPrint, kIsWeb;
import 'package:just_audio/just_audio.dart';

import 'package:asciineverdie/src/core/audio/sfx_channel_pool.dart';
import 'package:asciineverdie/src/core/storage/settings_repository.dart';

/// 게임 오디오 서비스 (싱글톤, 핫 리로드 안전)
///
/// BGM과 SFX를 관리하며, 설정과 연동하여 볼륨을 조절합니다.
/// 웹/WASM 환경에서는 제한적으로 작동할 수 있습니다.
///
/// 채널 구조:
/// - BGM: 단일 플레이어 (루프 재생)
/// - Player SFX: 플레이어 이펙트 (공격, 스킬, 아이템 등)
/// - Monster SFX: 몬스터 이펙트 (몬스터 공격 = 플레이어 피격)
///
/// 모든 플레이어 인스턴스를 static으로 관리하여 핫 리로드에서도 안전합니다.
class AudioService {
  // ─────────────────────────────────────────────────────────────────────────
  // 싱글톤 패턴
  // ─────────────────────────────────────────────────────────────────────────

  /// 싱글톤 인스턴스
  static AudioService? _instance;

  /// 싱글톤 인스턴스 반환
  static AudioService get instance {
    _instance ??= AudioService._internal(SettingsRepository());
    return _instance!;
  }

  /// 팩토리 생성자 (싱글톤 반환)
  factory AudioService({SettingsRepository? settingsRepository}) {
    _instance ??= AudioService._internal(
      settingsRepository ?? SettingsRepository(),
    );
    return _instance!;
  }

  /// private 생성자
  AudioService._internal(this._settingsRepository);

  final SettingsRepository _settingsRepository;

  // ─────────────────────────────────────────────────────────────────────────
  // static 플레이어 관리 (핫 리로드에서 유지)
  // ─────────────────────────────────────────────────────────────────────────

  /// static BGM 플레이어 (핫 리로드에서 유지)
  static AudioPlayer? _staticBgmPlayer;

  /// static 초기화 완료 여부
  static bool _staticInitialized = false;

  /// static 초기화 진행 중 Completer (중복 초기화 방지)
  static Completer<void>? _staticInitCompleter;

  // ─────────────────────────────────────────────────────────────────────────
  // 인스턴스 변수
  // ─────────────────────────────────────────────────────────────────────────

  // SFX 채널 풀 (채널별 분리, 완료 보장)
  SfxChannelPool? _playerSfxPool;
  SfxChannelPool? _monsterSfxPool;

  // 채널별 풀 크기 (배속 전투에서 사운드 누락 방지)
  static const int _playerPoolSize = 4;
  static const int _monsterPoolSize = 4;

  // 현재 볼륨
  double _bgmVolume = 0.7;
  double _sfxVolume = 0.8;

  // 현재 재생 중인 BGM
  String? _currentBgm;

  // 웹에서 사용자 상호작용 대기 중인 BGM (자동재생 정책 대응)
  String? _pendingBgm;

  // 사용자 상호작용 발생 여부 (웹 자동재생 정책 우회용)
  bool _userInteracted = false;

  // 오디오 일시정지 상태 (앱 백그라운드 시)
  bool _isPaused = false;

  // 일시정지 전 재생 중이던 BGM (복귀 시 재개용)
  String? _pausedBgm;

  // BGM 작업 진행 중 여부 (동시 호출 방지)
  bool _isBgmBusy = false;

  // 대기 중인 BGM (작업 중 새 요청이 들어온 경우)
  String? _queuedBgm;

  // ─────────────────────────────────────────────────────────────────────────
  // 초기화
  // ─────────────────────────────────────────────────────────────────────────

  /// 초기화 완료 여부
  bool get isInitialized => _staticInitialized;

  /// 서비스 초기화
  Future<void> init() async {
    // 이미 초기화 완료됨
    if (_staticInitialized) {
      return;
    }

    // 초기화 진행 중이면 완료 대기
    if (_staticInitCompleter != null) {
      await _staticInitCompleter!.future;
      return;
    }

    // 초기화 시작
    final completer = Completer<void>();
    _staticInitCompleter = completer;

    try {
      // 설정에서 볼륨 불러오기
      _bgmVolume = await _settingsRepository.loadBgmVolume();
      _sfxVolume = await _settingsRepository.loadSfxVolume();

      // BGM 플레이어 초기화 (순차적, 지연 포함)
      await _initBgmPlayer();

      // 지연 후 SFX 풀 초기화 (순차적)
      await Future<void>.delayed(const Duration(milliseconds: 200));
      await _initSfxPools();

      _staticInitialized = true;

      // 모바일/데스크톱에서는 자동재생 제한 없음
      if (!kIsWeb) {
        _userInteracted = true;
      }

      debugPrint('[AudioService] Initialized successfully');
      completer.complete();
    } catch (e) {
      debugPrint('[AudioService] Init error: $e');
      // 에러여도 완료 처리 (오디오 없이 게임 진행 가능)
      _staticInitialized = true;
      completer.complete();
    } finally {
      _staticInitCompleter = null;
    }
  }

  /// BGM 플레이어 초기화 (재시도 포함)
  Future<void> _initBgmPlayer() async {
    // 기존 플레이어가 있으면 재사용 (핫 리로드 대응)
    if (_staticBgmPlayer != null) {
      debugPrint('[AudioService] Reusing existing BGM player');
      try {
        await _staticBgmPlayer!.setVolume(_bgmVolume);
      } catch (_) {}
      return;
    }

    // 새 플레이어 생성 (재시도 포함)
    const maxRetries = 3;
    const baseDelay = Duration(milliseconds: 100);

    for (var attempt = 0; attempt < maxRetries; attempt++) {
      try {
        if (attempt > 0) {
          await Future<void>.delayed(baseDelay * (attempt + 1));
        }

        _staticBgmPlayer = AudioPlayer();
        await _staticBgmPlayer!.setLoopMode(LoopMode.one);
        await _staticBgmPlayer!.setVolume(_bgmVolume);
        debugPrint('[AudioService] BGM player created');
        return;
      } catch (e) {
        debugPrint(
          '[AudioService] BGM player attempt ${attempt + 1} failed: $e',
        );
        _staticBgmPlayer = null;
        if (attempt == maxRetries - 1) {
          debugPrint('[AudioService] BGM disabled');
        }
      }
    }
  }

  /// SFX 채널 풀 순차 초기화
  Future<void> _initSfxPools() async {
    // Player SFX 풀
    _playerSfxPool = SfxChannelPool(
      name: 'Player',
      poolSize: _playerPoolSize,
      volume: _sfxVolume,
    );
    await _playerSfxPool!.init();

    // 지연 후 Monster SFX 풀
    await Future<void>.delayed(const Duration(milliseconds: 200));

    _monsterSfxPool = SfxChannelPool(
      name: 'Monster',
      poolSize: _monsterPoolSize,
      volume: _sfxVolume,
    );
    await _monsterSfxPool!.init();
  }

  // ─────────────────────────────────────────────────────────────────────────
  // BGM 재생
  // ─────────────────────────────────────────────────────────────────────────

  /// BGM 재생 (동시 호출 보호)
  ///
  /// 여러 곳에서 동시에 호출되어도 마지막 요청만 처리합니다.
  /// 작업 중 새 요청이 들어오면 대기열에 저장하고 완료 후 재생합니다.
  Future<void> playBgm(String name) async {
    if (_isPaused) return;
    if (!_staticInitialized) await init();
    if (_bgmVolume == 0) return; // 볼륨 0이면 재생 안함
    if (_currentBgm == name) return;
    if (_staticBgmPlayer == null) return;

    // 작업 중이면 대기열에 저장 (마지막 요청만 유지)
    if (_isBgmBusy) {
      _queuedBgm = name;
      debugPrint('[AudioService] BGM $name queued');
      return;
    }

    _isBgmBusy = true;
    try {
      await _playBgmInternal(name);
    } finally {
      _isBgmBusy = false;
      // 대기 중인 BGM이 있으면 재생
      if (_queuedBgm != null && _queuedBgm != _currentBgm) {
        final queued = _queuedBgm;
        _queuedBgm = null;
        await playBgm(queued!);
      }
    }
  }

  /// 내부 BGM 재생 (뮤텍스 내에서 호출)
  Future<void> _playBgmInternal(String name) async {
    final assetPath = 'assets/audio/bgm/$name.mp3';

    try {
      // 이전 BGM이 있을 때만 stop() 호출
      if (_currentBgm != null) {
        debugPrint('[AudioService] Stopping previous BGM: $_currentBgm');
        await _staticBgmPlayer!.stop();
      }

      debugPrint('[AudioService] Loading BGM: $assetPath');
      await _staticBgmPlayer!.setAsset(assetPath);

      debugPrint('[AudioService] Starting BGM playback');
      await _staticBgmPlayer!.play();

      _currentBgm = name;
      _pendingBgm = null;
      _userInteracted = true;
      debugPrint('[AudioService] Playing BGM: $name');
    } on PlayerInterruptedException catch (e) {
      // 다른 BGM 요청으로 인한 중단 - 정상적인 상황
      debugPrint('[AudioService] BGM $name interrupted: ${e.message}');
    } catch (e) {
      final errorStr = e.toString();

      // "Loading interrupted"는 새 BGM 요청으로 인한 정상 중단
      if (errorStr.contains('Loading interrupted') ||
          errorStr.contains('abort')) {
        debugPrint(
          '[AudioService] BGM $name loading interrupted (new request)',
        );
        return; // 플레이어 재생성 불필요
      }

      debugPrint('[AudioService] BGM error: $errorStr');

      // macOS Operation Stopped 에러: 플레이어 재생성 후 재시도
      if (errorStr.contains('Operation Stopped') ||
          errorStr.contains('-11849')) {
        debugPrint('[AudioService] Recreating BGM player...');
        await _recreateBgmPlayer();

        if (_staticBgmPlayer != null) {
          try {
            await _staticBgmPlayer!.setAsset(assetPath);
            await _staticBgmPlayer!.play();
            _currentBgm = name;
            _userInteracted = true;
            debugPrint('[AudioService] Playing BGM: $name (after recreate)');
            return;
          } catch (retryError) {
            debugPrint('[AudioService] BGM retry failed: $retryError');
          }
        }
      } else if (kIsWeb && errorStr.contains('NotAllowedError')) {
        _pendingBgm = name;
        debugPrint('[AudioService] BGM $name pending (autoplay blocked)');
      } else {
        debugPrint('[AudioService] BGM play failed: $e');
      }
      _currentBgm = null;
    }
  }

  /// BGM 플레이어 재생성 (에러 복구용)
  Future<void> _recreateBgmPlayer() async {
    try {
      await _staticBgmPlayer?.dispose();
    } catch (_) {}
    _staticBgmPlayer = null;

    try {
      _staticBgmPlayer = AudioPlayer();
      await _staticBgmPlayer!.setLoopMode(LoopMode.one);
      await _staticBgmPlayer!.setVolume(_bgmVolume);
      debugPrint('[AudioService] BGM player recreated');
    } catch (e) {
      debugPrint('[AudioService] Failed to recreate BGM player: $e');
      _staticBgmPlayer = null;
    }
  }

  /// BGM 정지
  Future<void> stopBgm() async {
    if (_staticBgmPlayer == null) return;
    try {
      await _staticBgmPlayer!.stop();
    } catch (_) {}
    _currentBgm = null;
  }

  /// BGM 일시정지
  Future<void> pauseBgm() async {
    if (_staticBgmPlayer == null) return;
    try {
      await _staticBgmPlayer!.pause();
    } catch (_) {}
  }

  /// BGM 재개
  Future<void> resumeBgm() async {
    if (_staticBgmPlayer == null || _currentBgm == null) return;
    try {
      await _staticBgmPlayer!.play();
    } catch (_) {}
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 전체 오디오 제어
  // ─────────────────────────────────────────────────────────────────────────

  /// 전체 오디오 일시정지 (앱 백그라운드 시)
  Future<void> pauseAll() async {
    _isPaused = true;
    _pausedBgm = _currentBgm; // 복귀 시 재개를 위해 저장
    try {
      await _staticBgmPlayer?.stop();
    } catch (_) {}
    _currentBgm = null;
    debugPrint('[AudioService] All audio paused (was playing: $_pausedBgm)');
  }

  /// 전체 오디오 재개 (앱 포그라운드 복귀 시)
  Future<void> resumeAll() async {
    _isPaused = false;
    debugPrint('[AudioService] Audio resumed');
    // 일시정지 전 재생 중이던 BGM 재개
    if (_pausedBgm != null) {
      final bgmToResume = _pausedBgm!;
      _pausedBgm = null;
      await playBgm(bgmToResume);
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // SFX 재생
  // ─────────────────────────────────────────────────────────────────────────

  /// 플레이어 이펙트 SFX 재생
  Future<void> playPlayerSfx(String name) async {
    if (_isPaused) return;
    if (_sfxVolume == 0) return; // 볼륨 0이면 재생 안함
    if (!_staticInitialized) await init();
    _tryPlayPendingBgm();
    await _playerSfxPool?.play('assets/audio/sfx/$name.mp3');
  }

  /// 몬스터 이펙트 SFX 재생
  Future<void> playMonsterSfx(String name) async {
    if (_isPaused) return;
    if (_sfxVolume == 0) return; // 볼륨 0이면 재생 안함
    if (!_staticInitialized) await init();
    _tryPlayPendingBgm();
    await _monsterSfxPool?.play('assets/audio/sfx/$name.mp3');
  }

  /// 웹에서 대기 중인 BGM 재생 시도
  void _tryPlayPendingBgm() {
    if (!_userInteracted && _pendingBgm != null) {
      _userInteracted = true;
      final pending = _pendingBgm;
      _pendingBgm = null;
      playBgm(pending!);
    }
  }

  /// SFX 재생 (레거시 호환)
  @Deprecated('playPlayerSfx 또는 playMonsterSfx를 사용하세요.')
  Future<void> playSfx(String name) => playPlayerSfx(name);

  // ─────────────────────────────────────────────────────────────────────────
  // 볼륨 제어
  // ─────────────────────────────────────────────────────────────────────────

  /// BGM 볼륨 설정 (0.0 ~ 1.0)
  Future<void> setBgmVolume(double volume) async {
    _bgmVolume = volume.clamp(0.0, 1.0);
    if (_staticBgmPlayer != null) {
      try {
        // 볼륨 0이면 BGM 정지
        if (_bgmVolume == 0) {
          await _staticBgmPlayer!.stop();
          _currentBgm = null;
        } else {
          await _staticBgmPlayer!.setVolume(_bgmVolume);
        }
      } catch (_) {}
    }
    await _settingsRepository.saveBgmVolume(_bgmVolume);
  }

  /// SFX 볼륨 설정 (0.0 ~ 1.0)
  Future<void> setSfxVolume(double volume) async {
    _sfxVolume = volume.clamp(0.0, 1.0);
    await _playerSfxPool?.setVolume(_sfxVolume);
    await _monsterSfxPool?.setVolume(_sfxVolume);
    await _settingsRepository.saveSfxVolume(_sfxVolume);
  }

  /// 현재 BGM 볼륨
  double get bgmVolume => _bgmVolume;

  /// 현재 SFX 볼륨
  double get sfxVolume => _sfxVolume;

  /// 현재 재생 중인 BGM
  String? get currentBgm => _currentBgm;

  // ─────────────────────────────────────────────────────────────────────────
  // 유틸리티
  // ─────────────────────────────────────────────────────────────────────────

  /// 사용자 상호작용 발생 알림 (웹 자동재생 정책 우회)
  Future<void> notifyUserInteraction() async {
    if (_userInteracted) return;
    _userInteracted = true;
    if (_pendingBgm != null) {
      final pending = _pendingBgm;
      _pendingBgm = null;
      await playBgm(pending!);
    }
  }

  /// 서비스 정리
  Future<void> dispose() async {
    try {
      await _staticBgmPlayer?.dispose();
    } catch (_) {}
    _staticBgmPlayer = null;
    await _playerSfxPool?.dispose();
    await _monsterSfxPool?.dispose();
    _staticInitialized = false;
  }

  /// 모든 static 리소스 정리 (테스트용)
  static void resetAll() {
    try {
      _staticBgmPlayer?.dispose();
    } catch (_) {}
    _staticBgmPlayer = null;
    _staticInitialized = false;
    _staticInitCompleter = null;
    _instance = null;
    SfxChannelPool.resetAll();
  }
}

/// BGM 타입 열거형
enum BgmType {
  title,
  town,
  battle,
  battleAct4, // Act IV 전용 전투 BGM
  battleAct5, // Act V 전용 전투 BGM
  boss,
  actBoss, // Act 보스 전용 BGM
  elite, // 엘리트 몬스터 전투 BGM
  victory,
  death, // 사망 BGM
  actCinematic, // Act 전환 시네마틱 BGM
  ending, // 엔딩 BGM
}

/// SFX 타입 열거형
enum SfxType {
  attack,
  hit,
  skill,
  item,
  click,
  levelUp,
  questComplete,
  evade,
  block,
  parry,
}

/// BgmType을 파일명으로 변환
extension BgmTypeExtension on BgmType {
  String get fileName => switch (this) {
    BgmType.title => 'title',
    BgmType.town => 'town',
    BgmType.battle => 'battle',
    BgmType.battleAct4 => 'battle_act4',
    BgmType.battleAct5 => 'battle_act5',
    BgmType.boss => 'boss',
    BgmType.actBoss => 'act_boss',
    BgmType.elite => 'elite',
    BgmType.victory => 'victory',
    BgmType.death => 'death',
    BgmType.actCinematic => 'act_cinemetic', // 파일명 오타 유지
    BgmType.ending => 'ending',
  };
}

/// SfxType을 파일명으로 변환
extension SfxTypeExtension on SfxType {
  String get fileName => switch (this) {
    SfxType.attack => 'attack',
    SfxType.hit => 'hit',
    SfxType.skill => 'skill',
    SfxType.item => 'item',
    SfxType.click => 'click',
    SfxType.levelUp => 'level_up',
    SfxType.questComplete => 'quest_complete',
    SfxType.evade => 'evade',
    SfxType.block => 'block',
    SfxType.parry => 'parry',
  };
}
