import "server-only";

import { mkdirSync } from "node:fs";
import { dirname, resolve } from "node:path";
import Database from "better-sqlite3";
import { signedTokenAssetSchema, type SignedTokenAsset } from "@pons-agent/protocol";

export type StoredTokenAsset = SignedTokenAsset & { receivedAt: string };
export type StoredTokenAssetRecord = { asset: StoredTokenAsset; content: Buffer | null };

let database: Database.Database | null = null;

const ensureColumn = (db: Database.Database, name: string, definition: string) => {
  const columns = db.prepare("PRAGMA table_info(token_assets)").all() as Array<{ name: string }>;
  if (!columns.some((column) => column.name === name)) db.exec(`ALTER TABLE token_assets ADD COLUMN ${name} ${definition}`);
};

const getDatabase = () => {
  if (database) return database;

  const databasePath = resolve(process.env.AGENT_EVENT_DB_PATH ?? "data/agent-events.db");
  mkdirSync(dirname(databasePath), { recursive: true });
  database = new Database(databasePath);
  database.pragma("journal_mode = WAL");
  database.exec(`
    CREATE TABLE IF NOT EXISTS token_assets (
      asset_id TEXT PRIMARY KEY,
      version INTEGER NOT NULL DEFAULT 1,
      agent_id TEXT NOT NULL,
      created_at TEXT NOT NULL,
      chain_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      symbol TEXT NOT NULL,
      topic TEXT NOT NULL,
      signer TEXT NOT NULL,
      signature TEXT NOT NULL,
      mime_type TEXT,
      content_hash TEXT,
      byte_length INTEGER,
      width INTEGER,
      height INTEGER,
      model TEXT,
      content BLOB,
      received_at TEXT NOT NULL
    );
  `);
  ensureColumn(database, "version", "INTEGER NOT NULL DEFAULT 1");
  ensureColumn(database, "mime_type", "TEXT");
  ensureColumn(database, "content_hash", "TEXT");
  ensureColumn(database, "byte_length", "INTEGER");
  ensureColumn(database, "width", "INTEGER");
  ensureColumn(database, "height", "INTEGER");
  ensureColumn(database, "model", "TEXT");
  ensureColumn(database, "content", "BLOB");
  return database;
};

const rowToRecord = (row: Record<string, unknown>): StoredTokenAssetRecord => {
  const common = {
    assetId: String(row.assetId),
    agentId: String(row.agentId),
    createdAt: String(row.createdAt),
    chainId: 4663 as const,
    name: String(row.name),
    symbol: String(row.symbol),
    topic: String(row.topic),
    signer: String(row.signer),
    signature: String(row.signature),
  };
  const parsed = signedTokenAssetSchema.parse(Number(row.version) === 2 ? {
    version: 2,
    ...common,
    media: {
      mimeType: row.mimeType,
      contentHash: row.contentHash,
      byteLength: Number(row.byteLength),
      width: Number(row.width),
      height: Number(row.height),
      model: row.model,
    },
  } : { version: 1, ...common });
  return {
    asset: { ...parsed, receivedAt: String(row.receivedAt) },
    content: Buffer.isBuffer(row.content) ? row.content : null,
  };
};

export const getTokenAsset = (assetId: string): StoredTokenAssetRecord | undefined => {
  const row = getDatabase().prepare(`
    SELECT asset_id AS assetId, version, agent_id AS agentId, created_at AS createdAt,
      chain_id AS chainId, name, symbol, topic, signer, signature, mime_type AS mimeType,
      content_hash AS contentHash, byte_length AS byteLength, width, height, model, content,
      received_at AS receivedAt
    FROM token_assets WHERE asset_id = ?
  `).get(assetId) as Record<string, unknown> | undefined;
  return row ? rowToRecord(row) : undefined;
};

export const insertTokenAsset = (asset: SignedTokenAsset, content: Buffer | null) => {
  const existing = getTokenAsset(asset.assetId);
  if (existing) return { asset: existing.asset, created: false };
  if (asset.version === 2 && !content) throw new Error("Generated token asset is missing image content");

  const receivedAt = new Date().toISOString();
  getDatabase().prepare(`
    INSERT OR IGNORE INTO token_assets (
      asset_id, version, agent_id, created_at, chain_id, name, symbol, topic, signer, signature,
      mime_type, content_hash, byte_length, width, height, model, content, received_at
    ) VALUES (
      @assetId, @version, @agentId, @createdAt, @chainId, @name, @symbol, @topic, @signer, @signature,
      @mimeType, @contentHash, @byteLength, @width, @height, @model, @content, @receivedAt
    )
  `).run({
    ...asset,
    mimeType: asset.version === 2 ? asset.media.mimeType : null,
    contentHash: asset.version === 2 ? asset.media.contentHash : null,
    byteLength: asset.version === 2 ? asset.media.byteLength : null,
    width: asset.version === 2 ? asset.media.width : null,
    height: asset.version === 2 ? asset.media.height : null,
    model: asset.version === 2 ? asset.media.model : null,
    content,
    receivedAt,
  });
  const stored = getTokenAsset(asset.assetId);
  if (!stored) throw new Error("Token asset could not be stored");
  return { asset: stored.asset, created: stored.asset.signature === asset.signature };
};
