import { useState, useEffect, useCallback } from 'react'
import type { Call } from '@/types'

const dbName = 'pwa-db'
const storeName = 'subscriptions'

// アクセスキーのバックアップを通話履歴と同じ store に置くための予約 callId。
// Cookie は Android ホーム画面 PWA で消えることがあるが、IndexedDB は SW が
// push のたびに書き込むため残る。その耐久性に相乗りしてキーを退避する。
// update() の一覧には出さないようフィルタする。
const ACCESS_KEY_ID = '__accessKey__'

const openPwaDB = (): Promise<IDBDatabase> =>
	new Promise((resolve, reject) => {
		const openRequest = indexedDB.open(dbName, 1)
		openRequest.onupgradeneeded = () => {
			const db = openRequest.result
			if (!db.objectStoreNames.contains(storeName)) {
				db.createObjectStore(storeName, { keyPath: 'callId' })
			}
		}
		openRequest.onsuccess = () => resolve(openRequest.result)
		openRequest.onerror = () => reject(openRequest.error)
	})

export const saveAccessKeyToIDB = (accessKey: string): Promise<void> =>
	openPwaDB().then(db => new Promise<void>((resolve) => {
		const tx = db.transaction(storeName, 'readwrite')
		tx.objectStore(storeName).put({ callId: ACCESS_KEY_ID, accessKey })
		tx.oncomplete = () => resolve()
		tx.onerror = () => resolve()
	})).catch(() => {})

export const loadAccessKeyFromIDB = (): Promise<string | null> =>
	openPwaDB().then(db => new Promise<string | null>((resolve) => {
		const req = db.transaction(storeName, 'readonly').objectStore(storeName).get(ACCESS_KEY_ID)
		req.onsuccess = () => resolve(req.result?.accessKey ?? null)
		req.onerror = () => resolve(null)
	})).catch(() => null)

export const removeAccessKeyFromIDB = (): Promise<void> =>
	openPwaDB().then(db => new Promise<void>((resolve) => {
		const tx = db.transaction(storeName, 'readwrite')
		tx.objectStore(storeName).delete(ACCESS_KEY_ID)
		tx.oncomplete = () => resolve()
		tx.onerror = () => resolve()
	})).catch(() => {})


export const useIndexedDB = () => {
	const [latestCall, setLatestCall] = useState<Call | null>(null)
	const [callList, setCallList] = useState<Call[]>([])

	const update = useCallback(() => {
		const openRequest = indexedDB.open(dbName, 1)

		openRequest.onupgradeneeded = () => {
			const db = openRequest.result;
			if (!db.objectStoreNames.contains(storeName)) {
				db.createObjectStore(storeName, { keyPath: 'callId' })
			}
		}

		openRequest.onsuccess = () => {
			const db = openRequest.result
			const transaction = db.transaction(storeName, 'readonly')
			const store = transaction.objectStore(storeName)
			const request = store.openCursor(null, 'prev')

			request.onsuccess = event => {
				const cursor = request.result
				if (cursor) {
					// アクセスキーのバックアップレコードは通話履歴ではないのでスキップ
					if (cursor.value.callId === ACCESS_KEY_ID) {
						cursor.continue()
						return
					}
					// 最初のレコードが最新のレコードになる
					setLatestCall(cursor.value)
				} else {
					setLatestCall(null)
				}
			}

			// 全てのレコードを降順で取得
			const callListTemp: Call[] = []
			const cursorRequest = store.openCursor(null, 'prev')
			cursorRequest.onsuccess = event => {
				const cursor = cursorRequest.result
				if (cursor) {
					if (cursor.value.callId !== ACCESS_KEY_ID) callListTemp.push(cursor.value)
					cursor.continue()
				} else {
					setCallList(callListTemp)
				}
			}
		}

		openRequest.onerror = () => {
			console.error("Error opening IndexedDB:", openRequest.error)
		}
	}, [])

	const updateResponder = useCallback((callId: string, newResponder: string | null) => {
		const openRequest = indexedDB.open(dbName, 1)
	
		openRequest.onsuccess = () => {
			const db = openRequest.result
			const transaction = db.transaction(storeName, 'readwrite')
			const store = transaction.objectStore(storeName)
		
			const getRequest = store.get(callId)
		
			getRequest.onsuccess = () => {
				const record = getRequest.result
				if (record) {
					record.responder = newResponder
					const putRequest = store.put(record)
			
					putRequest.onsuccess = () => {
						console.log(`Call ID ${callId} updated successfully.`)
						update() // Update the state after successful update
					}
			
					putRequest.onerror = () => {
						console.error("Error updating record:", putRequest.error)
					}
				} else {
					console.error(`Record with Call ID ${callId} not found.`)
				}
			}
		
			getRequest.onerror = () => {
				console.error("Error retrieving record:", getRequest.error)
			}
		}
	
		openRequest.onerror = () => {
			console.error("Error opening IndexedDB:", openRequest.error)
		}
	}, [update])

	return {latestCall, callList, update, updateResponder}
}

export default useIndexedDB