package com.aletheia.app.data

import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.aletheia.app.model.Book
import com.aletheia.app.model.ReadingBookmark
import com.aletheia.app.model.ReadingNote
import com.aletheia.app.model.ReadingProgress

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

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(
            """
            CREATE TABLE books (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title TEXT NOT NULL,
                author TEXT NOT NULL,
                file_path TEXT NOT NULL,
                file_name TEXT NOT NULL,
                format TEXT NOT NULL,
                cover_image BLOB,
                reading_progress REAL NOT NULL DEFAULT 0,
                last_cfi TEXT,
                last_chapter TEXT,
                locations TEXT,
                date_added INTEGER NOT NULL,
                last_read INTEGER NOT NULL,
                total_pages INTEGER NOT NULL DEFAULT 0,
                current_page INTEGER NOT NULL DEFAULT 0,
                remote_id TEXT
            )
            """.trimIndent()
        )

        db.execSQL(
            """
            CREATE TABLE app_settings (
                key TEXT PRIMARY KEY,
                value TEXT NOT NULL
            )
            """.trimIndent()
        )

        db.execSQL(
            """
            CREATE TABLE reading_progress (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                book_id INTEGER NOT NULL,
                cfi TEXT,
                progress REAL NOT NULL,
                current_page INTEGER NOT NULL DEFAULT 0,
                chapter_title TEXT,
                timestamp INTEGER NOT NULL
            )
            """.trimIndent()
        )

        createBookmarksTable(db)
        createNotesTable(db)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        if (oldVersion < 2) {
            if (!hasColumn(db, "books", "calibre_id")) {
                db.execSQL("ALTER TABLE books ADD COLUMN calibre_id TEXT")
            }
        }
        if (oldVersion < 3) {
            createBookmarksTable(db)
        }
        if (oldVersion < 4) {
            createNotesTable(db)
        }
        if (oldVersion < 5) {
            migrateRemoteIdColumn(db)
        }
        if (oldVersion < 6) {
            if (!hasColumn(db, "reading_notes", "highlight_color")) {
                db.execSQL("ALTER TABLE reading_notes ADD COLUMN highlight_color TEXT")
            }
            if (!hasColumn(db, "reading_notes", "kind")) {
                db.execSQL("ALTER TABLE reading_notes ADD COLUMN kind TEXT NOT NULL DEFAULT 'note'")
            }
        }
    }

    fun getAllBooks(): List<Book> = readableDatabase.query(
        "books",
        null,
        null,
        null,
        null,
        null,
        "last_read DESC"
    ).use { cursor ->
        buildList {
            while (cursor.moveToNext()) {
                add(cursorToBook(cursor))
            }
        }
    }

    fun getBookById(id: Long): Book? = readableDatabase.query(
        "books",
        null,
        "id = ?",
        arrayOf(id.toString()),
        null,
        null,
        null,
        "1"
    ).use { cursor ->
        if (cursor.moveToFirst()) cursorToBook(cursor) else null
    }

    fun saveBook(book: Book): Long {
        val values = bookToValues(book)
        return if (book.id == 0L) {
            writableDatabase.insertOrThrow("books", null, values)
        } else {
            writableDatabase.update("books", values, "id = ?", arrayOf(book.id.toString()))
            book.id
        }
    }

    fun deleteBook(book: Book) {
        writableDatabase.beginTransaction()
        try {
            writableDatabase.delete("reading_progress", "book_id = ?", arrayOf(book.id.toString()))
            writableDatabase.delete("reading_bookmarks", "book_id = ?", arrayOf(book.id.toString()))
            writableDatabase.delete("reading_notes", "book_id = ?", arrayOf(book.id.toString()))
            writableDatabase.delete("books", "id = ?", arrayOf(book.id.toString()))
            writableDatabase.setTransactionSuccessful()
        } finally {
            writableDatabase.endTransaction()
        }
    }

    fun getSetting(key: String): String? = readableDatabase.query(
        "app_settings",
        arrayOf("value"),
        "key = ?",
        arrayOf(key),
        null,
        null,
        null,
        "1"
    ).use { cursor ->
        if (cursor.moveToFirst()) cursor.getString(0) else null
    }

    fun getSettings(keys: Collection<String>): Map<String, String> {
        if (keys.isEmpty()) return emptyMap()
        val orderedKeys = keys.distinct()
        val placeholders = orderedKeys.joinToString(",") { "?" }
        return readableDatabase.query(
            "app_settings",
            arrayOf("key", "value"),
            "key IN ($placeholders)",
            orderedKeys.toTypedArray(),
            null,
            null,
            null
        ).use { cursor ->
            buildMap {
                while (cursor.moveToNext()) put(cursor.getString(0), cursor.getString(1))
            }
        }
    }

    fun setSetting(key: String, value: String) {
        writableDatabase.insertWithOnConflict(
            "app_settings",
            null,
            ContentValues().apply {
                put("key", key)
                put("value", value)
            },
            SQLiteDatabase.CONFLICT_REPLACE
        )
    }

    fun setSettings(values: Map<String, String>) {
        if (values.isEmpty()) return
        val database = writableDatabase
        database.beginTransaction()
        try {
            values.forEach { (key, value) ->
                database.insertWithOnConflict(
                    "app_settings",
                    null,
                    ContentValues().apply {
                        put("key", key)
                        put("value", value)
                    },
                    SQLiteDatabase.CONFLICT_REPLACE
                )
            }
            database.setTransactionSuccessful()
        } finally {
            database.endTransaction()
        }
    }

    fun saveProgress(progress: ReadingProgress) {
        writableDatabase.beginTransaction()
        try {
            writableDatabase.insertOrThrow(
                "reading_progress",
                null,
                ContentValues().apply {
                    put("book_id", progress.bookId)
                    put("cfi", progress.cfi)
                    put("progress", progress.progress)
                    put("current_page", progress.currentPage)
                    put("chapter_title", progress.chapterTitle)
                    put("timestamp", progress.timestamp)
                }
            )

            writableDatabase.execSQL(
                """
                DELETE FROM reading_progress
                WHERE book_id = ?
                  AND id NOT IN (
                    SELECT id FROM reading_progress
                    WHERE book_id = ?
                    ORDER BY timestamp DESC
                    LIMIT ?
                  )
                """.trimIndent(),
                arrayOf(progress.bookId, progress.bookId, MAX_READING_HISTORY_ENTRIES)
            )

            writableDatabase.setTransactionSuccessful()
        } finally {
            writableDatabase.endTransaction()
        }
    }

    fun getLatestProgress(bookId: Long): ReadingProgress? = readableDatabase.query(
        "reading_progress",
        null,
        "book_id = ?",
        arrayOf(bookId.toString()),
        null,
        null,
        "timestamp DESC",
        "1"
    ).use { cursor ->
        if (cursor.moveToFirst()) {
            ReadingProgress(
                id = cursor.getLong(cursor.getColumnIndexOrThrow("id")),
                bookId = cursor.getLong(cursor.getColumnIndexOrThrow("book_id")),
                cfi = cursor.getString(cursor.getColumnIndexOrThrow("cfi")),
                progress = cursor.getDouble(cursor.getColumnIndexOrThrow("progress")),
                currentPage = cursor.getInt(cursor.getColumnIndexOrThrow("current_page")),
                chapterTitle = cursor.getString(cursor.getColumnIndexOrThrow("chapter_title")),
                timestamp = cursor.getLong(cursor.getColumnIndexOrThrow("timestamp"))
            )
        } else {
            null
        }
    }

    fun getBookmarks(bookId: Long): List<ReadingBookmark> = readableDatabase.query(
        "reading_bookmarks",
        null,
        "book_id = ?",
        arrayOf(bookId.toString()),
        null,
        null,
        "created_at DESC"
    ).use { cursor ->
        buildList {
            while (cursor.moveToNext()) {
                add(cursorToBookmark(cursor))
            }
        }
    }

    fun saveBookmark(bookmark: ReadingBookmark): Long {
        val values = ContentValues().apply {
            put("book_id", bookmark.bookId)
            put("cfi", bookmark.cfi)
            put("progress", bookmark.progress)
            put("current_page", bookmark.currentPage)
            put("total_pages", bookmark.totalPages)
            put("chapter_title", bookmark.chapterTitle)
            put("label", bookmark.label)
            put("created_at", bookmark.createdAt)
        }

        return if (bookmark.id == 0L) {
            writableDatabase.insertOrThrow("reading_bookmarks", null, values)
        } else {
            writableDatabase.update("reading_bookmarks", values, "id = ?", arrayOf(bookmark.id.toString()))
            bookmark.id
        }
    }

    fun deleteBookmark(bookmarkId: Long) {
        writableDatabase.delete("reading_bookmarks", "id = ?", arrayOf(bookmarkId.toString()))
    }

    fun getNotes(bookId: Long): List<ReadingNote> = readableDatabase.query(
        "reading_notes",
        null,
        "book_id = ?",
        arrayOf(bookId.toString()),
        null,
        null,
        "created_at DESC"
    ).use { cursor ->
        buildList {
            while (cursor.moveToNext()) {
                add(cursorToNote(cursor))
            }
        }
    }

    fun saveNote(note: ReadingNote): Long {
        val values = ContentValues().apply {
            put("book_id", note.bookId)
            put("cfi", note.cfi)
            put("progress", note.progress)
            put("current_page", note.currentPage)
            put("total_pages", note.totalPages)
            put("chapter_title", note.chapterTitle)
            put("selected_text", note.selectedText)
            put("note_text", note.noteText)
            put("created_at", note.createdAt)
            put("highlight_color", note.highlightColor)
            put("kind", note.kind)
        }

        return if (note.id == 0L) {
            writableDatabase.insertOrThrow("reading_notes", null, values)
        } else {
            writableDatabase.update("reading_notes", values, "id = ?", arrayOf(note.id.toString()))
            note.id
        }
    }

    fun deleteNote(noteId: Long) {
        writableDatabase.delete("reading_notes", "id = ?", arrayOf(noteId.toString()))
    }

    private fun bookToValues(book: Book) = ContentValues().apply {
        put("title", book.title)
        put("author", book.author)
        put("file_path", book.filePath)
        put("file_name", book.fileName)
        put("format", book.format)
        put("cover_image", book.coverImage)
        put("reading_progress", book.readingProgress)
        put("last_cfi", book.lastCfi)
        put("last_chapter", book.lastChapter)
        put("locations", book.locations)
        put("date_added", book.dateAdded)
        put("last_read", book.lastRead)
        put("total_pages", book.totalPages)
        put("current_page", book.currentPage)
        put("remote_id", book.remoteId)
    }

    private fun cursorToBook(cursor: Cursor) = Book(
        id = cursor.getLong(cursor.getColumnIndexOrThrow("id")),
        title = cursor.getString(cursor.getColumnIndexOrThrow("title")),
        author = cursor.getString(cursor.getColumnIndexOrThrow("author")),
        filePath = cursor.getString(cursor.getColumnIndexOrThrow("file_path")),
        fileName = cursor.getString(cursor.getColumnIndexOrThrow("file_name")),
        format = cursor.getString(cursor.getColumnIndexOrThrow("format")),
        coverImage = cursor.getBlob(cursor.getColumnIndexOrThrow("cover_image")),
        readingProgress = cursor.getDouble(cursor.getColumnIndexOrThrow("reading_progress")),
        lastCfi = cursor.getString(cursor.getColumnIndexOrThrow("last_cfi")),
        lastChapter = cursor.getString(cursor.getColumnIndexOrThrow("last_chapter")),
        locations = cursor.getString(cursor.getColumnIndexOrThrow("locations")),
        dateAdded = cursor.getLong(cursor.getColumnIndexOrThrow("date_added")),
        lastRead = cursor.getLong(cursor.getColumnIndexOrThrow("last_read")),
        totalPages = cursor.getInt(cursor.getColumnIndexOrThrow("total_pages")),
        currentPage = cursor.getInt(cursor.getColumnIndexOrThrow("current_page")),
        remoteId = cursor.getString(cursor.getColumnIndexOrThrow("remote_id"))
    )

    private fun cursorToBookmark(cursor: Cursor) = ReadingBookmark(
        id = cursor.getLong(cursor.getColumnIndexOrThrow("id")),
        bookId = cursor.getLong(cursor.getColumnIndexOrThrow("book_id")),
        cfi = cursor.getString(cursor.getColumnIndexOrThrow("cfi")),
        progress = cursor.getDouble(cursor.getColumnIndexOrThrow("progress")),
        currentPage = cursor.getInt(cursor.getColumnIndexOrThrow("current_page")),
        totalPages = cursor.getInt(cursor.getColumnIndexOrThrow("total_pages")),
        chapterTitle = cursor.getString(cursor.getColumnIndexOrThrow("chapter_title")),
        label = cursor.getString(cursor.getColumnIndexOrThrow("label")),
        createdAt = cursor.getLong(cursor.getColumnIndexOrThrow("created_at"))
    )

    private fun cursorToNote(cursor: Cursor) = ReadingNote(
        id = cursor.getLong(cursor.getColumnIndexOrThrow("id")),
        bookId = cursor.getLong(cursor.getColumnIndexOrThrow("book_id")),
        cfi = cursor.getString(cursor.getColumnIndexOrThrow("cfi")),
        progress = cursor.getDouble(cursor.getColumnIndexOrThrow("progress")),
        currentPage = cursor.getInt(cursor.getColumnIndexOrThrow("current_page")),
        totalPages = cursor.getInt(cursor.getColumnIndexOrThrow("total_pages")),
        chapterTitle = cursor.getString(cursor.getColumnIndexOrThrow("chapter_title")),
        selectedText = cursor.getString(cursor.getColumnIndexOrThrow("selected_text")),
        noteText = cursor.getString(cursor.getColumnIndexOrThrow("note_text")),
        createdAt = cursor.getLong(cursor.getColumnIndexOrThrow("created_at")),
        highlightColor = cursor.getString(cursor.getColumnIndexOrThrow("highlight_color")),
        kind = cursor.getString(cursor.getColumnIndexOrThrow("kind"))
    )

    private fun createBookmarksTable(db: SQLiteDatabase) {
        db.execSQL(
            """
            CREATE TABLE IF NOT EXISTS reading_bookmarks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                book_id INTEGER NOT NULL,
                cfi TEXT,
                progress REAL NOT NULL,
                current_page INTEGER NOT NULL DEFAULT 0,
                total_pages INTEGER NOT NULL DEFAULT 0,
                chapter_title TEXT,
                label TEXT NOT NULL,
                created_at INTEGER NOT NULL
            )
            """.trimIndent()
        )
        db.execSQL(
            """
            CREATE INDEX IF NOT EXISTS idx_reading_bookmarks_book_id
            ON reading_bookmarks(book_id, created_at DESC)
            """.trimIndent()
        )
    }

    private fun createNotesTable(db: SQLiteDatabase) {
        db.execSQL(
            """
            CREATE TABLE IF NOT EXISTS reading_notes (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                book_id INTEGER NOT NULL,
                cfi TEXT,
                progress REAL NOT NULL,
                current_page INTEGER NOT NULL DEFAULT 0,
                total_pages INTEGER NOT NULL DEFAULT 0,
                chapter_title TEXT,
                selected_text TEXT,
                note_text TEXT NOT NULL,
                created_at INTEGER NOT NULL,
                highlight_color TEXT,
                kind TEXT NOT NULL DEFAULT 'note'
            )
            """.trimIndent()
        )
        db.execSQL(
            """
            CREATE INDEX IF NOT EXISTS idx_reading_notes_book_id
            ON reading_notes(book_id, created_at DESC)
            """.trimIndent()
        )
    }

    private fun migrateRemoteIdColumn(db: SQLiteDatabase) {
        if (!hasColumn(db, "books", "remote_id")) {
            db.execSQL("ALTER TABLE books ADD COLUMN remote_id TEXT")
        }
        if (hasColumn(db, "books", "calibre_id")) {
            db.execSQL("UPDATE books SET remote_id = COALESCE(remote_id, calibre_id)")
        }
    }

    private fun hasColumn(db: SQLiteDatabase, tableName: String, columnName: String): Boolean {
        db.rawQuery("PRAGMA table_info($tableName)", null).use { cursor ->
            while (cursor.moveToNext()) {
                if (cursor.getString(cursor.getColumnIndexOrThrow("name")) == columnName) {
                    return true
                }
            }
        }
        return false
    }

    companion object {
        private const val DATABASE_NAME = "aletheia.db3"
        private const val DATABASE_VERSION = 6
        private const val MAX_READING_HISTORY_ENTRIES = 200
    }
}
