import fs from "node:fs"
import path from "node:path"
import crypto from "node:crypto"

import { Level } from "level"

export const CONTENT_PACK_DEFINITIONS = [
  { sourceFile: "armes.json", outputFolder: "armes", type: "Item" },
  { sourceFile: "armures.json", outputFolder: "armures", type: "Item" },
  { sourceFile: "equipements.json", outputFolder: "equipements", type: "Item" },
  { sourceFile: "pouvoirs-compagnie.json", outputFolder: "pouvoirs-compagnie", type: "Item" },
  { sourceFile: "competences.json", outputFolder: "competences", type: "Item" },
  { sourceFile: "races.json", outputFolder: "races", type: "Item" },
  { sourceFile: "tribus.json", outputFolder: "tribus", type: "Item" },
  { sourceFile: "metiers.json", outputFolder: "metiers", type: "Item" },
  { sourceFile: "sortileges.json", outputFolder: "sortileges", type: "Item" },
]

export const SYSTEM_PACK_DEFINITIONS = [
  ...CONTENT_PACK_DEFINITIONS,
  { sourceFile: "aide-systeme.json", outputFolder: "aide-systeme", type: "JournalEntry" },
]

function slugId(input) {
  const hash = crypto.createHash("sha256").update(input).digest()
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  let id = ""
  for (let index = 0; id.length < 16; index += 1) {
    id += alphabet[hash[index % hash.length] % alphabet.length]
  }
  return id
}

function createDocumentStats({
  documentSystemId,
  documentSystemVersion,
  coreVersion,
  createdTime,
  lastModifiedBy = "Copilot",
} = {}) {
  return {
    systemId: documentSystemId,
    systemVersion: documentSystemVersion,
    coreVersion,
    createdTime,
    modifiedTime: createdTime,
    lastModifiedBy,
    compendiumSource: null,
    duplicateSource: null,
    exportSource: null,
  }
}

function toItemPackDocument(entry, index, options = {}) {
  const docId = slugId(`${entry.type}:${entry.name}`)
  return {
    key: `!items!${docId}`,
    value: {
      name: entry.name,
      type: entry.type,
      img: entry.img ?? "icons/svg/item-bag.svg",
      system: entry.system ?? {},
      effects: Array.isArray(entry.effects) ? entry.effects : [],
      flags: entry.flags ?? {},
      _stats: createDocumentStats(options),
      _id: docId,
      folder: null,
      sort: index * 1000,
      ownership: {
        default: 0,
      },
    },
    embedded: [],
  }
}

function toJournalPageDocument(entry, journalId, page, pageIndex, options = {}) {
  const pageId = slugId(`JournalEntryPage:${entry.name}:${page.name ?? page.title ?? pageIndex}`)
  return {
    _id: pageId,
    name: page.name ?? `Page ${pageIndex + 1}`,
    type: page.type ?? "text",
    title: {
      show: page.title?.show ?? true,
      level: page.title?.level ?? 1,
    },
    text: {
      format: page.text?.format ?? 1,
      content: page.text?.content ?? "",
    },
    system: page.system ?? {},
    image: page.image ?? {},
    video: page.video ?? { controls: true, volume: 0.5 },
    src: page.src ?? null,
    category: page.category ?? null,
    sort: page.sort ?? pageIndex * 1000,
    ownership: page.ownership ?? { default: -1 },
    flags: page.flags ?? {},
    _stats: createDocumentStats(options),
    _key: `!journal.pages!${journalId}.${pageId}`,
  }
}

function toJournalPackDocument(entry, index, options = {}) {
  const docId = slugId(`JournalEntry:${entry.name}`)
  const pages = Array.isArray(entry.pages)
    ? entry.pages.map((page, pageIndex) => toJournalPageDocument(entry, docId, page, pageIndex, options))
    : []

  return {
    key: `!journal!${docId}`,
    value: {
      name: entry.name,
      pages: pages.map((page) => page._id),
      ownership: entry.ownership ?? { default: 0 },
      flags: entry.flags ?? { core: {} },
      _stats: createDocumentStats(options),
      folder: null,
      sort: entry.sort ?? index * 1000,
      _id: docId,
      categories: Array.isArray(entry.categories) ? entry.categories : [],
    },
    embedded: pages.map((page) => ({
      key: page._key,
      value: {
        name: page.name,
        type: page.type,
        title: page.title,
        text: page.text,
        system: page.system,
        image: page.image,
        video: page.video,
        src: page.src,
        category: page.category,
        sort: page.sort,
        ownership: page.ownership,
        flags: page.flags,
        _stats: page._stats,
        _id: page._id,
      },
    })),
  }
}

function toPackDocument(entry, index, options = {}) {
  if (entry.type === "JournalEntry") return toJournalPackDocument(entry, index, options)
  return toItemPackDocument(entry, index, options)
}

async function buildPack({
  sourcePath,
  outputPath,
  type,
  documentSystemId,
  documentSystemVersion,
  coreVersion,
  createdTime,
  lastModifiedBy,
}) {
  const source = JSON.parse(fs.readFileSync(sourcePath, "utf8"))
  if (!Array.isArray(source)) {
    throw new Error(`Pack source must be an array: ${sourcePath}`)
  }

  fs.rmSync(outputPath, { recursive: true, force: true })
  fs.mkdirSync(outputPath, { recursive: true })

  const db = new Level(outputPath, { valueEncoding: "utf8" })

  try {
    await db.open()
    const batch = db.batch()
    source.forEach((entry, index) => {
      if (!entry.type) {
        throw new Error(`Missing document type in ${sourcePath}: ${entry.name}`)
      }
      const doc = toPackDocument(entry, index, {
        documentSystemId,
        documentSystemVersion,
        coreVersion,
        createdTime,
        lastModifiedBy,
      })
      batch.put(doc.key, JSON.stringify(doc.value))
      for (const embedded of doc.embedded) {
        batch.put(embedded.key, JSON.stringify(embedded.value))
      }
    })
    await batch.write()
  } finally {
    await db.close()
  }
}

export async function buildPacks({
  sourceRoot,
  outputRoot,
  packDefinitions = SYSTEM_PACK_DEFINITIONS,
  documentSystemId,
  documentSystemVersion,
  coreVersion,
  createdTime = Date.now(),
  lastModifiedBy = "Copilot",
}) {
  for (const pack of packDefinitions) {
    await buildPack({
      sourcePath: path.join(sourceRoot, pack.sourceFile),
      outputPath: path.join(outputRoot, pack.outputFolder),
      type: pack.type,
      documentSystemId,
      documentSystemVersion,
      coreVersion,
      createdTime,
      lastModifiedBy,
    })
  }
}
