package com.aletheia.app.ui.audiobook

import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.SeekBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.lifecycleScope
import com.aletheia.app.AletheiaApplication
import com.aletheia.app.R
import com.aletheia.app.databinding.ActivityAudioBookPlayerBinding
import com.aletheia.app.model.AudioBook
import com.aletheia.app.ui.reader.ReaderActivity
import com.aletheia.app.util.setBookCover
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONArray

class AudioBookPlayerActivity : AppCompatActivity() {
    private lateinit var binding: ActivityAudioBookPlayerBinding
    private val app by lazy { application as AletheiaApplication }
    private val handler = Handler(Looper.getMainLooper())
    private var mediaPlayer: MediaPlayer? = null
    private var audioBook: AudioBook? = null
    private var chapters: List<Chapter> = emptyList()
    private var prepared = false
    private var userSeeking = false
    private var speedIndex = 1
    private var sleepAtMs: Long? = null

    private val progressUpdater = object : Runnable {
        override fun run() {
            updateProgress()
            handler.postDelayed(this, 500)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityAudioBookPlayerBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupControls()
        val id = intent.getLongExtra(EXTRA_AUDIOBOOK_ID, 0L)
        if (id <= 0) finish() else loadAudioBook(id)
    }

    override fun onResume() {
        super.onResume()
        handler.post(progressUpdater)
    }

    override fun onPause() {
        savePosition()
        handler.removeCallbacks(progressUpdater)
        super.onPause()
    }

    override fun onDestroy() {
        handler.removeCallbacksAndMessages(null)
        mediaPlayer?.release()
        mediaPlayer = null
        super.onDestroy()
    }

    private fun setupControls() {
        binding.playerBackButton.setOnClickListener { finish() }
        binding.playerPlayButton.setOnClickListener { togglePlayback() }
        binding.playerRewindButton.setOnClickListener { seekBy(-15_000) }
        binding.playerForwardButton.setOnClickListener { seekBy(30_000) }
        binding.playerSpeedButton.setOnClickListener { cycleSpeed() }
        binding.playerSleepButton.setOnClickListener { showSleepTimer() }
        binding.playerReadButton.setOnClickListener {
            audioBook?.let { startActivity(Intent(this, ReaderActivity::class.java).putExtra(ReaderActivity.EXTRA_BOOK_ID, it.sourceBookId)) }
        }
        binding.playerMoreButton.setOnClickListener { anchor ->
            PopupMenu(this, anchor).apply {
                menu.add("Начать сначала").setOnMenuItemClickListener {
                    mediaPlayer?.seekTo(0)
                    updateProgress()
                    true
                }
                menu.add("Открыть электронную книгу").setOnMenuItemClickListener {
                    binding.playerReadButton.performClick()
                    true
                }
                show()
            }
        }
        binding.playerSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onStartTrackingTouch(seekBar: SeekBar?) { userSeeking = true }
            override fun onStopTrackingTouch(seekBar: SeekBar?) {
                val duration = mediaPlayer?.duration?.takeIf { it > 0 } ?: 0
                mediaPlayer?.seekTo(duration * (seekBar?.progress ?: 0) / 1000)
                userSeeking = false
                updateProgress()
            }
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) = Unit
        })
    }

    private fun loadAudioBook(id: Long) {
        lifecycleScope.launch {
            val data = withContext(Dispatchers.IO) {
                val audio = app.audioBookRepository.getById(id)
                val book = audio?.let { app.bookRepository.getBookById(it.sourceBookId) }
                audio to book
            }
            val audio = data.first
            if (audio == null || audio.status != AudioBook.Status.READY || !File(audio.filePath).isFile) {
                Toast.makeText(this@AudioBookPlayerActivity, "Файл аудиокниги недоступен", Toast.LENGTH_LONG).show()
                finish()
                return@launch
            }
            audioBook = audio
            chapters = parseChapters(audio.chaptersJson)
            binding.playerTitle.text = audio.title
            binding.playerAuthor.text = audio.author
            binding.playerCover.setBookCover(data.second?.coverImage, R.drawable.default_cover)
            preparePlayer(audio)
        }
    }

    private fun preparePlayer(audio: AudioBook) {
        mediaPlayer = MediaPlayer().apply {
            setAudioAttributes(
                AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .build()
            )
            setDataSource(audio.filePath)
            setOnPreparedListener { player ->
                prepared = true
                player.seekTo(audio.positionMs.coerceAtMost(player.duration.toLong()).toInt())
                applySpeed()
                updateProgress()
            }
            setOnCompletionListener {
                binding.playerPlayButton.text = "▶"
                updateProgress()
                savePosition()
            }
            setOnErrorListener { _, _, _ ->
                Toast.makeText(this@AudioBookPlayerActivity, "Не удалось воспроизвести M4A-файл", Toast.LENGTH_LONG).show()
                true
            }
            prepareAsync()
        }
    }

    private fun togglePlayback() {
        val player = mediaPlayer ?: return
        if (!prepared) return
        if (player.isPlaying) player.pause() else player.start()
        binding.playerPlayButton.text = if (player.isPlaying) "Ⅱ" else "▶"
    }

    private fun seekBy(deltaMs: Int) {
        val player = mediaPlayer ?: return
        if (!prepared) return
        player.seekTo((player.currentPosition + deltaMs).coerceIn(0, player.duration))
        updateProgress()
    }

    private fun cycleSpeed() {
        speedIndex = (speedIndex + 1) % SPEEDS.size
        applySpeed()
    }

    private fun applySpeed() {
        val speed = SPEEDS[speedIndex]
        binding.playerSpeedButton.text = "${speed.toDisplay()}x"
        if (prepared) runCatching {
            mediaPlayer?.playbackParams = mediaPlayer!!.playbackParams.setSpeed(speed)
        }
    }

    private fun showSleepTimer() {
        val labels = arrayOf("Выключен", "15 минут", "30 минут", "45 минут", "60 минут")
        val minutes = intArrayOf(0, 15, 30, 45, 60)
        MaterialAlertDialogBuilder(this)
            .setTitle("Таймер сна")
            .setItems(labels) { _, index ->
                sleepAtMs = minutes[index].takeIf { it > 0 }?.let { System.currentTimeMillis() + it * 60_000L }
                binding.playerSleepButton.alpha = if (sleepAtMs == null) 1f else 0.45f
            }
            .show()
    }

    private fun updateProgress() {
        val player = mediaPlayer ?: return
        if (!prepared) return
        sleepAtMs?.let { deadline ->
            if (System.currentTimeMillis() >= deadline) {
                if (player.isPlaying) player.pause()
                binding.playerPlayButton.text = "▶"
                sleepAtMs = null
                binding.playerSleepButton.alpha = 1f
            }
        }
        val position = player.currentPosition.coerceAtLeast(0)
        val duration = player.duration.coerceAtLeast(1)
        if (!userSeeking) binding.playerSeekBar.progress = position * 1000 / duration
        binding.playerElapsed.text = formatTime(position.toLong())
        binding.playerRemaining.text = "-${formatTime((duration - position).toLong())}"
        binding.playerRemainingSummary.text = remainingSummary((duration - position).toLong())
        binding.playerChapter.text = chapters.lastOrNull { it.startMs <= position }?.title ?: "Вся книга"
    }

    private fun savePosition() {
        val id = audioBook?.id ?: return
        val position = mediaPlayer?.currentPosition?.toLong() ?: return
        app.applicationScope.launch { app.audioBookRepository.savePosition(id, position) }
    }

    private fun parseChapters(json: String): List<Chapter> = runCatching {
        val array = JSONArray(json)
        buildList {
            for (index in 0 until array.length()) {
                val item = array.getJSONObject(index)
                add(Chapter(item.getString("title"), item.getLong("startMs")))
            }
        }
    }.getOrDefault(emptyList())

    private fun remainingSummary(milliseconds: Long): String {
        val totalMinutes = milliseconds.coerceAtLeast(0) / 60_000
        val hours = totalMinutes / 60
        val minutes = totalMinutes % 60
        return if (hours > 0) "$hours ч $minutes мин до конца книги" else "$minutes мин до конца книги"
    }

    private fun formatTime(milliseconds: Long): String {
        val seconds = milliseconds.coerceAtLeast(0) / 1000
        val hours = seconds / 3600
        val minutes = (seconds % 3600) / 60
        val rest = seconds % 60
        return if (hours > 0) "%d:%02d:%02d".format(hours, minutes, rest) else "%02d:%02d".format(minutes, rest)
    }

    private fun Float.toDisplay(): String = if (this % 1f == 0f) toInt().toString() else
        String.format(Locale.US, "%.2f", this).trimEnd('0').trimEnd('.')

    private data class Chapter(val title: String, val startMs: Long)

    companion object {
        private const val EXTRA_AUDIOBOOK_ID = "audio_book_id"
        private val SPEEDS = floatArrayOf(0.75f, 1f, 1.25f, 1.5f, 2f)

        fun createIntent(context: Context, audioBookId: Long) =
            Intent(context, AudioBookPlayerActivity::class.java).putExtra(EXTRA_AUDIOBOOK_ID, audioBookId)
    }
}
