import AudioToolbox
import AVFAudio
import Foundation
import os

@MainActor
final class CallSoundManager {

    static let shared = CallSoundManager()

    private var loopingPlayer: AVAudioPlayer?
    private var oneShotPlayer: AVAudioPlayer?
    private var vibrationTask: Task<Void, Never>?

    private init() {}

    // MARK: - Public API

    func playRingtone() {
        stopAll()
        configureForRingtone()
        playLoop("ringtone")
        startVibration()
    }

    func playCalling() {
        stopAll()
        configureForRingtone()
        playLoop("calling")
    }

    func playConnected() {
        stopLooping()
        stopVibration()
        playOneShot("connected")
    }

    func playEndCall() {
        stopLooping()
        stopVibration()
        playOneShot("end_call")
    }

    func stopAll() {
        stopLooping()
        stopVibration()
        oneShotPlayer?.stop()
        oneShotPlayer = nil
    }

    // MARK: - Audio Session

    private func configureForRingtone() {
        let session = AVAudioSession.sharedInstance()
        // Use .playAndRecord so iOS keeps the audio session active in background
        // (requires UIBackgroundModes: audio in Info.plist).
        // .defaultToSpeaker routes ringtone through speaker.
        // WebRTC will reconfigure the session later when call connects.
        try? session.setCategory(
            .playAndRecord,
            mode: .default,
            options: [.defaultToSpeaker, .mixWithOthers]
        )
        try? session.setActive(true)
    }

    // MARK: - Playback

    private func playLoop(_ name: String) {
        guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
            callLogger.warning("[CallSound] Sound file not found: \(name, privacy: .public).mp3")
            return
        }
        do {
            let player = try AVAudioPlayer(contentsOf: url)
            player.numberOfLoops = -1
            player.volume = 1.0
            player.prepareToPlay()
            player.play()
            loopingPlayer = player
        } catch {
            callLogger.error("[CallSound] Failed to play \(name, privacy: .public): \(error.localizedDescription, privacy: .public)")
        }
    }

    private func playOneShot(_ name: String) {
        guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
            callLogger.warning("[CallSound] Sound file not found: \(name, privacy: .public).mp3")
            return
        }
        do {
            let player = try AVAudioPlayer(contentsOf: url)
            player.numberOfLoops = 0
            player.volume = 1.0
            player.prepareToPlay()
            player.play()
            oneShotPlayer = player
        } catch {
            callLogger.error("[CallSound] Failed to play \(name, privacy: .public): \(error.localizedDescription, privacy: .public)")
        }
    }

    private func stopLooping() {
        loopingPlayer?.stop()
        loopingPlayer = nil
    }

    // MARK: - Vibration

    private func startVibration() {
        stopVibration()
        vibrationTask = Task {
            while !Task.isCancelled {
                AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
                // Short sleep for responsive cancellation
                for _ in 0..<9 {
                    guard !Task.isCancelled else { return }
                    try? await Task.sleep(for: .milliseconds(200))
                }
            }
        }
    }

    private func stopVibration() {
        vibrationTask?.cancel()
        vibrationTask = nil
    }
}
