package org.foxarmy.barcodescannerforemployees.activities

import android.Manifest
import android.content.ContentValues
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.BaseColumns
import android.util.Log
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import org.foxarmy.barcodescannerforemployees.*
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import kotlin.concurrent.thread

class AddAbstractProductActivity : AppCompatActivity() {
    private lateinit var imageView: ImageView

    private lateinit var saveButton: Button
    private lateinit var takePictureButton: Button
    private lateinit var scanButton: Button

    private lateinit var barcodeText: EditText
    private lateinit var productNameText: TextView
    private lateinit var netWeightText: TextView
    private lateinit var unitTypeSpinner: Spinner

    private lateinit var categorySpinner: Spinner

    private var abstractProduct: AbstractProduct? = null
    private lateinit var pictureFile: File
    private lateinit var picturesPath: File
    private var barcode: String = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.fragment_add_abstract_product)

        val extras = intent.extras
        abstractProduct = extras!!.get("abstractProduct") as AbstractProduct?
        if (abstractProduct != null) {
            barcode = abstractProduct!!.barcode
        }



        picturesPath = File(filesDir, "pictures")
        val thumbnailsDir = File(cacheDir, "thumbnails")
        thumbnailsDir.mkdirs()
        picturesPath.mkdirs()
        imageView = findViewById(R.id.imageView)

        saveButton = findViewById(R.id.saveButton)
        takePictureButton = findViewById(R.id.takePictureButton)
        scanButton = findViewById(R.id.scan_button)

        barcodeText = findViewById(R.id.barcodeTextEdit)
        productNameText = findViewById(R.id.productName)
        netWeightText = findViewById(R.id.netWeight)
        unitTypeSpinner = findViewById(R.id.unitTypeSpinner)

        categorySpinner = findViewById(R.id.categorySpinner)

        fillupCategorySpinner()
        fillupUnitsSpinner()

        barcodeText.addTextChangedListener {
            this.barcode = barcodeText.text.toString()
        }

        if (abstractProduct?.name == "" && abstractProduct?.barcode != "") {
            performRequest(abstractProduct?.barcode!!)
        }

        if (abstractProduct != null) {
            val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp"))
            pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]")
            imageView.setImageURI(imageThumbnailUri)
            imageView.rotation = 90f
            barcodeText.setText(abstractProduct!!.barcode)
            productNameText.text = abstractProduct!!.name
            netWeightText.text = abstractProduct!!.netWeight.toString()
            categorySpinner.setSelection(abstractProduct!!.category)
            unitTypeSpinner.setSelection(abstractProduct!!.unit)
        }

        saveButton.setOnClickListener {
            val productName = productNameText.text.toString()
            val netWeight = netWeightText.text
            if (abstractProduct == null && (!this::pictureFile.isInitialized || !pictureFile.exists())) {
                Toast.makeText(this, "Please, make a picture of a product!", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            if (barcode == "") {
                Toast.makeText(this, "Please, scan barcode on a product", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            if (productName == "") {
                Toast.makeText(this, "Please, write a name of a product!", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) {
                Toast.makeText(this, "Please, write a valid net weight of a product!", Toast.LENGTH_SHORT).show()
            }

            val db = DBStorageController(this).writableDatabase
            val values = ContentValues().apply {
                put(AbstractProductContract.AbstractProductEntry.BARCODE, barcode)
                put(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, productName)
                put(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, netWeight.toString())
                put(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, pictureFile.nameWithoutExtension)
                put(AbstractProductContract.AbstractProductEntry.CATEGORY, categorySpinner.selectedItemPosition)
                put(AbstractProductContract.AbstractProductEntry.UNIT, unitTypeSpinner.selectedItemPosition)
            }

            if (abstractProduct == null) {
                db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values)
            } else {
                db.update(AbstractProductContract.AbstractProductEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(abstractProduct!!.id.toString()))
            }

            finish()
        }

        takePictureButton.setOnClickListener {
            requestPermissionLauncher.launch(Manifest.permission.CAMERA)
        }

        scanButton.setOnClickListener {
            val options = GmsBarcodeScannerOptions.Builder()
                .setBarcodeFormats(
                    Barcode.FORMAT_EAN_13
                )
                .build()
            val scanner = GmsBarcodeScanning.getClient(this)
            scanner.startScan()
                .addOnSuccessListener { barcode ->
                    this.barcode = barcode.rawValue.toString()

                    performRequest(this.barcode)
                }
                .addOnFailureListener { e ->
                    Toast.makeText(
                        this,
                        "Failed to scan barcode. Please, try again or enter data manually",
                        Toast.LENGTH_LONG
                    ).show()
                }
        }
    }

    fun performRequest(barcode: String) {
        barcodeText.setText(this.barcode)
        val requester = Requester("https://ean-online.ru", "match.php")
        requester.request(this, barcode)
        var abstractProduct: AbstractProduct

        if (DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, this.barcode) != null) {
            AlertDialog.Builder(this)
                .setMessage("You've got an abstract product with such barcode in your database")
                .setPositiveButton("Quit") { _: DialogInterface, _: Int ->
                    finish()
                }
                .setNegativeButton("Edit existing") { _: DialogInterface, _: Int ->
                    val addProductIntent = Intent(this, AddAbstractProductActivity::class.java)
                    val extras = Bundle()
                    val existingAbstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, this.barcode)
                    extras.putParcelable("abstractProduct", existingAbstractProduct)
                    addProductIntent.putExtras(extras)
                    ContextCompat.startActivity(this, addProductIntent, extras)
                    finish()
                }.show()
        }

        thread {
            // Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :))
            while (requester.response == "") { }
            if (requester.response == "Not found 404") {
                runOnUiThread {
                    Toast.makeText(this, "Product not found. Please, try again or type manually", Toast.LENGTH_LONG).show()
                }
                return@thread
            }

            abstractProduct = Parser().parse(requester.response)
            requester.response = ""
            runOnUiThread {
                productNameText.text = abstractProduct.name
                netWeightText.text = abstractProduct.netWeight.toString()
                unitTypeSpinner.setSelection(abstractProduct.unit)
            }
        }
    }

    private fun fillupUnitsSpinner() {
        val units = listOf(
            getString(R.string.kilogram),
            getString(R.string.gram),
            getString(R.string.liter),
            getString(R.string.milliliter),
            getString(R.string.pieces)
        )


        val arrayAdapter = ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units)
        arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
        unitTypeSpinner.adapter = arrayAdapter
    }

    fun fillupCategorySpinner() {
        val db = DBStorageController(this).readableDatabase

        val categories = mutableListOf("")

        val projection = arrayOf(
            CategoriesContract.CategoryEntry.CATEGORY_NAME
        )

        val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, null, null, null, null, BaseColumns._ID+" ASC")

        with (cursor) {
            while (moveToNext()) {
                categories.add(getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME)))
            }
        }

        val arrayAdapter = ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories)
        arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
        categorySpinner.adapter = arrayAdapter
    }

    @RequiresApi(Build.VERSION_CODES.R)
    val takePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) { success: Boolean ->
        if (success) {
            //Move picture to a proper directory according to its calculated hash
            val tempfile = File(filesDir, "image.png")
            val imageContent = tempfile.inputStream().readBytes()
            val imageHash = imageContent.toString(Charsets.UTF_8).md5()

            pictureFile = File(picturesPath, "$imageHash.png")
            Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
            tempfile.delete()
            generateThumbnailForImage(this, imageHash)

            imageView.setImageURI(getImageUri(this, pictureFile))
        } else {
            Log.e("QWERTYUIOP", "Cannot save a picture")
        }
    }

    @RequiresApi(Build.VERSION_CODES.R)
    fun getPicture () {
        //Saving picture to a temp file for further hash calculation and moving to a proper directory
        val imageFile = File(this.filesDir, "image.png")
        val imageUri = getImageUri(this, imageFile)
        if (imageUri != null) {
            takePicture.launch(imageUri)
        }
    }

    @RequiresApi(Build.VERSION_CODES.R)
    val requestPermissionLauncher =
        registerForActivityResult(
            ActivityResultContracts.RequestPermission()
        ) { isGranted: Boolean ->
            if (isGranted) {
                getPicture()
            } else {
                Toast.makeText(this, "I need permission in order to take a picture", Toast.LENGTH_LONG).show()
            }
        }
}