/*
 * Copyright (c) 2023 Samson Achiaga
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.certified.audionote.ui

import android.Manifest
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.DatePicker
import android.widget.TimePicker
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.navigation.fragment.navArgs
import com.certified.audionote.R
import com.certified.audionote.databinding.DialogEditReminderBinding
import com.certified.audionote.databinding.FragmentAddNoteBinding
import com.certified.audionote.model.Note
import com.certified.audionote.utils.Extensions.safeNavigate
import com.certified.audionote.utils.Extensions.showKeyboardFor
import com.certified.audionote.utils.Extensions.showToast
import com.certified.audionote.utils.ReminderAvailableState
import com.certified.audionote.utils.cancelAlarm
import com.certified.audionote.utils.currentDate
import com.certified.audionote.utils.filePath
import com.certified.audionote.utils.formatReminderDate
import com.certified.audionote.utils.roundOffDecimal
import com.certified.audionote.utils.startAlarm
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import timerx.*
import java.io.File
import java.io.IOException
import java.util.Calendar
import java.util.concurrent.TimeUnit

@AndroidEntryPoint
class AddNoteFragment : Fragment(), DatePickerDialog.OnDateSetListener,
    TimePickerDialog.OnTimeSetListener {

    private var _binding: FragmentAddNoteBinding? = null
    private val binding get() = _binding!!

    private val viewModel: NotesViewModel by viewModels()
    private lateinit var navController: NavController

    private val args: EditNoteFragmentArgs by navArgs()
    private lateinit var _note: Note
    private var isRecording = false
    private var pickedDateTime: Calendar? = null
    private val currentDateTime by lazy { currentDate() }
    private var mediaRecorder: MediaRecorder? = null
    private var file: File? = null
    private var stopWatch: Stopwatch? = null
    private var timer: Timer? = null

    private val requestAudioRecordingPermissionLauncher =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
            if (!isGranted) MaterialAlertDialogBuilder(requireContext()).apply {
                setTitle(getString(R.string.audio_record_permission))
                setMessage(getString(R.string.permission_required))
                setPositiveButton(getString(R.string.ok)) { dialog, _ -> dialog.dismiss() }
                show()
            }
            else startRecording()
        }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        _binding = FragmentAddNoteBinding.inflate(layoutInflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        navController = Navigation.findNavController(view)

        binding.lifecycleOwner = viewLifecycleOwner
        binding.viewModel = viewModel
        args.note.let {
            _note = it
            binding.note = it
        }

        binding.btnBack.setOnClickListener {
            if (binding.etNoteTitle.text.toString().isNotBlank())
                saveNote()
            else
                try {
                    file?.delete()
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            navController.safeNavigate(AddNoteFragmentDirections.actionAddNoteFragmentToHomeFragment())
        }
        binding.cardAddReminder.setOnClickListener {
            if (viewModel.reminderAvailableState.value == ReminderAvailableState.NO_REMINDER)
                pickDate()
            else
                openEditReminderDialog()
        }
        binding.btnRecord.setOnClickListener { recordAudio() }
        binding.fabSaveNote.setOnClickListener {
            if (binding.etNoteTitle.text.toString().isNotBlank())
                saveNote()
            else {
                showToast(requireContext().getString(R.string.title_required))
                binding.etNoteTitle.requestFocus()
            }
        }
    }

    override fun onResume() {
        super.onResume()
        binding.etNoteTitle.apply {
            requestFocus()
            showKeyboardFor(requireContext())
        }
        updateStatusBarColor(binding.note!!.color)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        mediaRecorder = null
        timer?.apply {
            stop()
            reset()
        }
        timer = null
        stopWatch?.apply {
            stop()
            reset()
        }
        stopWatch = null
        _binding = null
    }

    private fun recordAudio() {
        binding.apply {
            if (!isRecording) {
                if (ContextCompat.checkSelfPermission(
                        requireContext(), Manifest.permission.RECORD_AUDIO
                    ) == PackageManager.PERMISSION_GRANTED
                ) {
                    btnRecord.setImageDrawable(
                        ResourcesCompat.getDrawable(
                            resources,
                            R.drawable.ic_mic_recording,
                            null
                        )
                    ).run {
                        try {
                            file?.delete()
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                        isRecording = true
                        startRecording()
                    }
                } else if (shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO))
                    MaterialAlertDialogBuilder(requireContext()).apply {
                        setTitle(getString(R.string.audio_record_permission))
                        setMessage(getString(R.string.permission_required))
                        setPositiveButton(getString(R.string.ok)) { dialog, _ -> dialog.dismiss() }
                        show()
                    }
                else requestAudioRecordingPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
            } else {
                btnRecord.setImageDrawable(
                    ResourcesCompat.getDrawable(
                        resources, R.drawable.ic_mic_not_recording, null
                    )
                )
                    .run {
                        isRecording = false
                        stopRecording()
                    }
            }
        }
    }

    override fun onDateSet(p0: DatePicker?, p1: Int, p2: Int, p3: Int) {
        pickedDateTime = currentDate()
        pickedDateTime!!.set(p1, p2, p3)
        val hourOfDay = currentDateTime.get(Calendar.HOUR_OF_DAY)
        val minuteOfDay = currentDateTime.get(Calendar.MINUTE)
        val timePickerDialog =
            TimePickerDialog(requireContext(), this, hourOfDay, minuteOfDay, false)
        timePickerDialog.setOnDismissListener {
            viewModel._reminderAvailableState.value = ReminderAvailableState.HAS_REMINDER
            _note.reminder = pickedDateTime!!.timeInMillis
            binding.tvReminderDate.text = formatReminderDate(pickedDateTime!!.timeInMillis)
        }
        timePickerDialog.show()
    }

    override fun onTimeSet(p0: TimePicker?, p1: Int, p2: Int) {
        pickedDateTime!!.set(Calendar.HOUR_OF_DAY, p1)
        pickedDateTime!!.set(Calendar.MINUTE, p2)
        if (pickedDateTime!!.timeInMillis <= currentDate().timeInMillis) {
            pickedDateTime!!.run {
                set(Calendar.DAY_OF_MONTH, currentDateTime.get(Calendar.DAY_OF_MONTH) + 1)
                set(Calendar.YEAR, currentDateTime.get(Calendar.YEAR))
                set(Calendar.MONTH, currentDateTime.get(Calendar.MONTH))
            }
        }
    }

    private fun pickDate() {
        val startYear = currentDateTime.get(Calendar.YEAR)
        val startMonth = currentDateTime.get(Calendar.MONTH)
        val startDay = currentDateTime.get(Calendar.DAY_OF_MONTH)
        val datePickerDialog =
            DatePickerDialog(requireContext(), this, startYear, startMonth, startDay)
        datePickerDialog.show()
    }

    private fun openEditReminderDialog() {
        val view = DialogEditReminderBinding.inflate(layoutInflater)
        val bottomSheetDialog = BottomSheetDialog(requireContext())
        view.apply {
            note = _note
            btnDeleteReminder.setOnClickListener {
                viewModel._reminderAvailableState.value = ReminderAvailableState.NO_REMINDER
                _note.reminder = null
                bottomSheetDialog.dismiss()
            }
            btnModifyReminder.setOnClickListener {
                bottomSheetDialog.dismiss()
                pickDate()
            }
        }
        bottomSheetDialog.edgeToEdgeEnabled
        bottomSheetDialog.setContentView(view.root)
        bottomSheetDialog.show()
    }

    private fun saveNote() {
        binding.apply {
            stopRecording()
            if (_note.audioLength <= 0) {
                showToast(requireContext().getString(R.string.record_note_before_saving))
                return
            }
            val note = _note.copy(
                title = etNoteTitle.text.toString().trim(),
                description = etNoteDescription.text.toString().trim()
            )
            this@AddNoteFragment.viewModel.insertNote(note)
            showToast(requireContext().getString(R.string.note_saved))
            if (pickedDateTime?.timeInMillis != null && pickedDateTime!!.timeInMillis <= currentDateTime.timeInMillis)
                startAlarm(requireContext(), pickedDateTime!!.timeInMillis, note)
            navController.safeNavigate(AddNoteFragmentDirections.actionAddNoteFragmentToHomeFragment())
        }
    }

    private fun startRecording() {
        val filePath = filePath(requireActivity())
        val fileName = "${System.currentTimeMillis()}.3gp"
        _note.filePath = "$filePath/$fileName"

        stopWatch = buildStopwatch {
            startFormat("MM:SS")
            onTick { millis: Long, time: CharSequence->
                binding.tvTimer.text = time
            }
            changeFormatWhen(1, TimeUnit.HOURS, "HH:MM:SS")
        }
        

        mediaRecorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setOutputFile("$filePath/$fileName")
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            try {
                prepare()
                start()
                stopWatch!!.start()
                file = File("$filePath/$fileName")
            } catch (e: IOException) {
                showToast(requireContext().getString(R.string.error_occurred))
            }
        }
    }

    private fun stopRecording() {
        mediaRecorder?.apply {
            stop()
            release()
        }
        mediaRecorder = null
        stopWatch?.apply {
            stop()
            _note.audioLength = stopWatch!!.currentTimeInMillis / 1000
            reset()
        }
        stopWatch = null
        if (_note.audioLength <= 0)
            return
        file = File(_note.filePath)
        val fileByte = (file!!.readBytes().size.toDouble() / 1048576.00)
        val fileSize = roundOffDecimal(fileByte)
        _note.size = fileSize
    }

    private fun updateStatusBarColor(color: Int) {
        val window = requireActivity().window
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
//        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        window.statusBarColor = color
    }
}