import { mkdir } from "node:fs/promises"
import { dirname, join } from "node:path"
import { DateTime } from "luxon"

const set = async <T extends keyof Keys>(key: T, value: Keys[T]) => {
  try {
    await writeStore(key, value)
    return value
  } catch (error) {
    console.error(`Error storing key "${key}":`, error)
    throw error
  }
}

const update = async <T extends keyof Keys>(
  key: T,
  defaultValue: Keys[T],
  updateFn: (prev: Keys[T]) => Keys[T] | Promise<Keys[T]>
) => {
  try {
    const currentValue = await get(key, defaultValue)
    const newValue = await updateFn(currentValue)
    return await set(key, newValue)
  } catch (error) {
    console.error(`Error updating key "${key}":`, error)
    throw error
  }
}

async function get<T extends keyof Keys>(key: T, defaultValue: Keys[T]): Promise<Keys[T]>
async function get<T extends keyof Keys>(key: T, defaultValue: undefined): Promise<Keys[T] | undefined> {
  try {
    return (await readStore(key)) ?? defaultValue
  } catch (error) {
    console.error(`Error retrieving key "${key}":`, error)
    return defaultValue
  }
}

const remove = async <T extends keyof Keys>(key: T): Promise<void> => {
  try {
    const keyPath = getStorePath(key)
    const file = Bun.file(keyPath)
    if (await file.exists()) {
      await file.delete()
    }
  } catch (error) {
    console.error(`Error removing key "${key}":`, error)
    throw error
  }
}

const readStore = async <T extends keyof Keys>(key: T): Promise<Keys[T] | undefined> => {
  try {
    const keyPath = getStorePath(key)
    const file = Bun.file(keyPath)
    if (!(await file.exists())) {
      return undefined
    }
    const data = await file.text()
    return JSON.parse(data)
  } catch (error) {
    console.error(`Error reading store for key "${key}":`, error)
    return undefined
  }
}

const writeStore = async <T extends keyof Keys>(key: T, value: Keys[T]): Promise<void> => {
  try {
    const keyPath = getStorePath(key)
    await mkdir(dirname(keyPath), { recursive: true })
    await Bun.write(keyPath, JSON.stringify(value, null, 2))
  } catch (error) {
    console.error(`Error writing store for key "${key}":`, error)
    throw error
  }
}

const getStorePath = <T extends keyof Keys>(key: T): string => {
  const rootDir = process.env.DATA_DIR ?? join(import.meta.dir, "..")
  const kvDir = join(rootDir, "data/kv")
  const version = keyVersions[key]
  return join(kvDir, `${key}.v${version}.json`)
}

export default { set, get, remove, update }

export type Keys = {
  threads: Record<string, TrackedThread> // threadId: thread metadata
  todos: Record<string, string> // todoId: todoText
  cronJobs: Record<string, CronJobHistory[]> // jobId: array of job runs
}

export type CronJobHistory = {
  error?: string
  stderr?: string
  stdout?: string
  runAt: string
}

export type TrackedThread = {
  threadId: string
  threadName: string
}

const keyVersions: Record<keyof Keys, number> = {
  threads: 1,
  todos: 1,
  cronJobs: 1,
} as const

export type Conversation = { message: string; role: "user" | "assistant" }
