package org.foxarmy.barcodescannerforemployees

import android.content.ContentValues
import android.content.Context
import android.database.DatabaseUtils
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.provider.BaseColumns
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import java.io.File

object AbstractProductContract {
    object AbstractProductEntry : BaseColumns {
        const val TABLE_NAME = "abstract_products"
        const val BARCODE = "barcode"
        const val PRODUCT_NAME = "name"
        const val PRODUCT_NET_WEIGHT = "net_weight"
        const val IMAGE_FILENAME = "image_filename"
        const val CATEGORY = "category"
        const val UNIT = "unit"
    }
}

object CategoriesContract {
    object CategoryEntry : BaseColumns {
        const val TABLE_NAME = "categories"
        const val CATEGORY_NAME = "category_name"
    }
}

object ProductContract {
    object ProductEntry : BaseColumns {
        const val TABLE_NAME = "products"
        const val ABSTRACT_PRODUCT_ID = "abstract_product_id"
        const val AMOUNT = "amount"
        const val DATE_OF_PRODUCTION = "date_of_production"
        const val EXPIRY_DATE = "expiry_date"
    }
}

const val SQL_CREATE_ABSTRACT_PRODUCTS_TABLE =
    "CREATE TABLE ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} (" +
            "${BaseColumns._ID} INTEGER PRIMARY KEY," +
            "${AbstractProductContract.AbstractProductEntry.BARCODE} TEXT," +
            "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} TEXT," +
            "${AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT} REAL," +
            "${AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME} TEXT," +
            "${AbstractProductContract.AbstractProductEntry.CATEGORY} INTEGER," +
            "${AbstractProductContract.AbstractProductEntry.UNIT} INTEGER)"

const val SQL_CREATE_PRODUCTS_TABLE =
    "CREATE TABLE ${ProductContract.ProductEntry.TABLE_NAME} (" +
        "${BaseColumns._ID} INTEGER PRIMARY KEY," +
        "${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID} INTEGER," +
        "${ProductContract.ProductEntry.AMOUNT} INTEGER," +
        "${ProductContract.ProductEntry.DATE_OF_PRODUCTION} INTEGER," +
        "${ProductContract.ProductEntry.EXPIRY_DATE} INTGER)"

const val SQL_CREATE_CATEGORIES_TABLE =
    "CREATE TABLE ${CategoriesContract.CategoryEntry.TABLE_NAME} (" +
            "${BaseColumns._ID} INTEGER PRIMARY KEY," +
            "${CategoriesContract.CategoryEntry.CATEGORY_NAME} TEXT)"

class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(SQL_CREATE_ABSTRACT_PRODUCTS_TABLE)
        db.execSQL(SQL_CREATE_PRODUCTS_TABLE)
        db.execSQL(SQL_CREATE_CATEGORIES_TABLE)
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldV: Int, newV: Int) {
        TODO("Not yet implemented")
    }

    fun eraseCategory (db: SQLiteDatabase, id: Int, context: Context) {
        val productsInCategory = getAllAbstractProductInCategory(db, id)

        for (product in productsInCategory.iterator()) {
            eraseAbstractProduct(db, product.id, context)
        }

        db.delete(CategoriesContract.CategoryEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
    }

    fun getCategoryNameById(db: SQLiteDatabase, id: Int) : String {
        var result = ""

        val projection = arrayOf(CategoriesContract.CategoryEntry.CATEGORY_NAME)
        val selection = "${BaseColumns._ID} = ?"
        val selectionArgs = arrayOf(id.toString())

        val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)

        with (cursor) {
            while (moveToNext()) {
                result = getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
            }
        }

        return result
    }

    fun getAllAbstractProductInCategory(db: SQLiteDatabase, id: Int) : List<AbstractProduct> {

        var result = mutableListOf<AbstractProduct>()
        val projection = arrayOf(
            BaseColumns._ID,
            AbstractProductContract.AbstractProductEntry.BARCODE,
            AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
            AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
            AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
            AbstractProductContract.AbstractProductEntry.UNIT
        )

        val selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
        val selectionArgs = arrayOf(id.toString())

        val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)

        with(cursor) {
            while (moveToNext()) {
                val abstractProductId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
                val abstractProductBarcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
                val abstractProductName = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
                val abstractProductNetWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
                val abstractProductImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
                val abstractProductUnit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))

                val abstractProduct = AbstractProduct(abstractProductId, abstractProductBarcode, abstractProductName, abstractProductNetWeight, abstractProductImageHash, category = id, abstractProductUnit)

                result.add(abstractProduct)
            }
        }

        return result
    }

    fun getAmountOfAbstractProductsInCategory(db:SQLiteDatabase, id: Int) : Int {
        return DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?", arrayOf(id.toString())).toInt()
    }
    fun eraseAbstractProduct(db: SQLiteDatabase, id: Int, context: Context) {
        val projectionForAbstractProduct = arrayOf(
            AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME
        )

        val selectionForAbstractProduct = "${BaseColumns._ID} = ?"
        val selectionArgsForAbstractProduct = arrayOf(id.toString())
        val cursorForAbstractProduct = db.query(
            AbstractProductContract.AbstractProductEntry.TABLE_NAME,
            projectionForAbstractProduct,
            selectionForAbstractProduct,
            selectionArgsForAbstractProduct,
            null,
            null,
            null
        )
        var imageHash: String = ""

        with (cursorForAbstractProduct) {
            while(moveToNext()) {
                val productImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
                imageHash = productImageHash
            }
        }
        val picturesDir = File(context.filesDir, "pictures")
        val thumbnailsDir = File(context.cacheDir, "thumbnails")

        File(picturesDir, "$imageHash.png").delete()
        File(thumbnailsDir, "$imageHash.webp").delete()

        val projectionForProducts = arrayOf(BaseColumns._ID)
        val selectionForProducts = "${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID} = ?"
        val selectionArgsForProducts = arrayOf(id.toString())

        val cursorForProducts = db.query(ProductContract.ProductEntry.TABLE_NAME, projectionForProducts, selectionForProducts, selectionArgsForProducts, null, null, null)

        with (cursorForProducts) {
            while(moveToNext()) {
                eraseProduct(db, getInt(getColumnIndexOrThrow(BaseColumns._ID)))
            }
        }

        db.delete(AbstractProductContract.AbstractProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
    }

    fun eraseProduct(db:SQLiteDatabase, id: Int) {
        db.delete(ProductContract.ProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
    }

    fun insertNewProduct(db: SQLiteDatabase, product: Product) {
        val values = ContentValues().apply {
            put(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID, product.abstractProductId)
            put(ProductContract.ProductEntry.AMOUNT, product.amount)
            put(ProductContract.ProductEntry.DATE_OF_PRODUCTION, product.dateOfProduction)
            put(ProductContract.ProductEntry.EXPIRY_DATE, product.dateOfExpiry)
        }

        db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
    }

    fun findAbstractProductByBarcode (db: SQLiteDatabase, barcode: String) : AbstractProduct? {
        var abstractProduct: AbstractProduct? = null
        val projection = arrayOf(
            BaseColumns._ID,
            AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
            AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
            AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
            AbstractProductContract.AbstractProductEntry.CATEGORY,
            AbstractProductContract.AbstractProductEntry.UNIT
        )

        val selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = ?"
        val selectionArgs = arrayOf(barcode)

        val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)

        with(cursor) {
            while(moveToNext()) {
                val id = getInt(getColumnIndexOrThrow(BaseColumns._ID))
                val productName = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
                val imageFilename = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
                val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
                val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
                val unit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))

                abstractProduct = AbstractProduct(id, barcode, productName, netWeight, imageFilename, category, unit)
            }
        }

        return abstractProduct
    }

    fun findAbstractProductById(db: SQLiteDatabase, id: Int): AbstractProduct? {
        var abstractProduct: AbstractProduct? = null
        val projection = arrayOf(
            BaseColumns._ID,
            AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
            AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
            AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
            AbstractProductContract.AbstractProductEntry.CATEGORY,
            AbstractProductContract.AbstractProductEntry.BARCODE,
            AbstractProductContract.AbstractProductEntry.UNIT
        )

        val selection = "${BaseColumns._ID} = ?"
        val selectionArgs = arrayOf(id.toString())

        val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)

        with (cursor) {
            while (moveToNext()) {
                val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
                val barcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
                val name = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
                val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
                val imageHash = getString((getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME)))
                val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
                val unit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))

                abstractProduct = AbstractProduct(productId, barcode, name, netWeight, imageHash, category, unit)
            }
        }

        return abstractProduct
    }

    fun updateProduct(db: SQLiteDatabase, product: Product) {
        val values = ContentValues().apply {
            put(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID, product.abstractProductId)
            put(ProductContract.ProductEntry.AMOUNT, product.amount)
            put(ProductContract.ProductEntry.DATE_OF_PRODUCTION, product.dateOfProduction)
            put(ProductContract.ProductEntry.EXPIRY_DATE, product.dateOfExpiry)
        }
        db.update(ProductContract.ProductEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(product.id.toString()))
    }

    companion object {
        const val DATABASE_VERSION = 1
        const val DATABASE_NAME = "database.db"
    }
}