import Database from "better-sqlite3";
import { Pool } from "pg";
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { logger } from "../monitoring/logger.js";
import * as zlib from "zlib";
import { promisify } from "util";
const gzipAsync = promisify(zlib.gzip);
const gunzipAsync = promisify(zlib.gunzip);
var StorageTier = /* @__PURE__ */ ((StorageTier2) => {
  StorageTier2["HOT"] = "hot";
  StorageTier2["COLD"] = "cold";
  return StorageTier2;
})(StorageTier || {});
const DEFAULT_SIMPLIFIED_CONFIG = {
  database: {
    type: process.env["DATABASE_URL"]?.startsWith("postgres") ? "postgresql" : "sqlite",
    url: process.env["DATABASE_URL"] || "./storage/stackmemory.db",
    maxConnections: 10
  },
  objectStorage: {
    endpoint: process.env["S3_ENDPOINT"] || "https://s3.amazonaws.com",
    bucket: process.env["S3_BUCKET"] || "stackmemory-archive",
    accessKeyId: process.env["S3_ACCESS_KEY_ID"] || "",
    secretAccessKey: process.env["S3_SECRET_ACCESS_KEY"] || "",
    region: process.env["S3_REGION"] || "us-east-1"
  },
  tiers: {
    archiveAfterDays: 30,
    // Archive after 30 days instead of complex 3-tier
    compressionThreshold: 1024
    // Compress items > 1KB
  }
};
class SimplifiedStorage {
  config;
  db;
  pgPool;
  s3Client;
  isInitialized = false;
  constructor(config = DEFAULT_SIMPLIFIED_CONFIG) {
    this.config = config;
    if (this.config.objectStorage.accessKeyId && this.config.objectStorage.secretAccessKey) {
      this.s3Client = new S3Client({
        endpoint: this.config.objectStorage.endpoint,
        region: this.config.objectStorage.region,
        credentials: {
          accessKeyId: this.config.objectStorage.accessKeyId,
          secretAccessKey: this.config.objectStorage.secretAccessKey
        }
      });
    }
  }
  async initialize() {
    if (this.isInitialized) return;
    try {
      if (this.config.database.type === "postgresql") {
        await this.initializePostgreSQL();
      } else {
        await this.initializeSQLite();
      }
      await this.createTables();
      this.isInitialized = true;
      this.startArchivalProcess();
      logger.info("Simplified storage initialized", {
        databaseType: this.config.database.type,
        objectStorageEnabled: !!this.s3Client
      });
    } catch (error) {
      logger.error("Failed to initialize simplified storage", { error });
      throw error;
    }
  }
  async initializePostgreSQL() {
    this.pgPool = new Pool({
      connectionString: this.config.database.url,
      max: this.config.database.maxConnections,
      idleTimeoutMillis: 3e4
    });
  }
  async initializeSQLite() {
    this.db = new Database(this.config.database.url);
    this.db.pragma("foreign_keys = ON");
    this.db.pragma("journal_mode = WAL");
  }
  async createTables() {
    const schema = `
      CREATE TABLE IF NOT EXISTS storage_items (
        id TEXT PRIMARY KEY,
        data BLOB,
        metadata TEXT,
        tier TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        last_accessed TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        compressed BOOLEAN DEFAULT FALSE,
        object_key TEXT -- For cold storage reference
      );
      CREATE INDEX IF NOT EXISTS idx_storage_tier ON storage_items(tier);
      CREATE INDEX IF NOT EXISTS idx_storage_created ON storage_items(created_at);
    `;
    if (this.pgPool) {
      await this.pgPool.query(schema.replace(/BLOB/g, "BYTEA").replace(/TIMESTAMP DEFAULT CURRENT_TIMESTAMP/g, "TIMESTAMP DEFAULT NOW()"));
    } else if (this.db) {
      this.db.exec(schema);
    }
  }
  /**
   * Store item in hot tier (database)
   */
  async store(id, data, metadata = {}) {
    if (!this.isInitialized) await this.initialize();
    let processedData = data;
    let compressed = false;
    if (data.length > this.config.tiers.compressionThreshold) {
      processedData = await gzipAsync(data);
      compressed = true;
      logger.debug("Compressed storage item", { id, originalSize: data.length, compressedSize: processedData.length });
    }
    const item = {
      id,
      data: processedData,
      metadata,
      tier: "hot" /* HOT */,
      createdAt: /* @__PURE__ */ new Date(),
      lastAccessed: /* @__PURE__ */ new Date(),
      compressed
    };
    if (this.pgPool) {
      await this.pgPool.query(
        `INSERT INTO storage_items (id, data, metadata, tier, created_at, last_accessed, compressed)
         VALUES ($1, $2, $3, $4, $5, $6, $7)
         ON CONFLICT (id) DO UPDATE SET
           data = EXCLUDED.data,
           metadata = EXCLUDED.metadata,
           last_accessed = EXCLUDED.last_accessed,
           compressed = EXCLUDED.compressed`,
        [id, processedData, JSON.stringify(metadata), item.tier, item.createdAt, item.lastAccessed, compressed]
      );
    } else if (this.db) {
      const stmt = this.db.prepare(`
        INSERT OR REPLACE INTO storage_items (id, data, metadata, tier, created_at, last_accessed, compressed)
        VALUES (?, ?, ?, ?, ?, ?, ?)
      `);
      stmt.run(id, processedData, JSON.stringify(metadata), item.tier, item.createdAt?.toISOString(), item.lastAccessed?.toISOString(), compressed ? 1 : 0);
    }
    logger.debug("Stored item in hot tier", { id, size: processedData.length, compressed });
  }
  /**
   * Retrieve item from appropriate tier
   */
  async retrieve(id) {
    if (!this.isInitialized) await this.initialize();
    await this.updateLastAccessed(id);
    let row;
    if (this.pgPool) {
      const { rows } = await this.pgPool.query("SELECT * FROM storage_items WHERE id = $1", [id]);
      row = rows[0];
    } else if (this.db) {
      row = this.db.prepare("SELECT * FROM storage_items WHERE id = ?").get(id);
    }
    if (!row) return null;
    let data;
    if (row.tier === "cold" /* COLD */ && row.object_key && this.s3Client) {
      data = await this.retrieveFromColdStorage(row.object_key);
    } else {
      data = row.data;
    }
    if (row.compressed) {
      data = await gunzipAsync(data);
    }
    logger.debug("Retrieved item", { id, tier: row.tier, compressed: row.compressed });
    return data;
  }
  /**
   * Archive old items to cold storage
   */
  async archiveOldItems() {
    if (!this.s3Client) return;
    const cutoffDate = /* @__PURE__ */ new Date();
    cutoffDate.setDate(cutoffDate.getDate() - this.config.tiers.archiveAfterDays);
    let rows;
    if (this.pgPool) {
      const { rows: pgRows } = await this.pgPool.query(
        "SELECT * FROM storage_items WHERE tier = $1 AND created_at < $2 LIMIT 100",
        ["hot" /* HOT */, cutoffDate]
      );
      rows = pgRows;
    } else if (this.db) {
      rows = this.db.prepare(
        "SELECT * FROM storage_items WHERE tier = ? AND created_at < ? LIMIT 100"
      ).all("hot" /* HOT */, cutoffDate.toISOString());
    } else {
      return;
    }
    for (const row of rows) {
      try {
        await this.archiveItem(row);
        logger.debug("Archived item to cold storage", { id: row.id });
      } catch (error) {
        logger.warn("Failed to archive item", { id: row.id, error });
      }
    }
    if (rows.length > 0) {
      logger.info("Archived old items", { count: rows.length, cutoffDate });
    }
  }
  async archiveItem(row) {
    if (!this.s3Client) return;
    const objectKey = `archived/${row.id}`;
    await this.s3Client.send(
      new PutObjectCommand({
        Bucket: this.config.objectStorage.bucket,
        Key: objectKey,
        Body: row.data,
        Metadata: {
          originalId: row.id,
          compressed: row.compressed.toString(),
          archivedAt: (/* @__PURE__ */ new Date()).toISOString()
        }
      })
    );
    if (this.pgPool) {
      await this.pgPool.query(
        "UPDATE storage_items SET tier = $1, data = NULL, object_key = $2 WHERE id = $3",
        ["cold" /* COLD */, objectKey, row.id]
      );
    } else if (this.db) {
      this.db.prepare(
        "UPDATE storage_items SET tier = ?, data = NULL, object_key = ? WHERE id = ?"
      ).run("cold" /* COLD */, objectKey, row.id);
    }
  }
  async retrieveFromColdStorage(objectKey) {
    if (!this.s3Client) throw new Error("Object storage not configured");
    const response = await this.s3Client.send(
      new GetObjectCommand({
        Bucket: this.config.objectStorage.bucket,
        Key: objectKey
      })
    );
    if (!response.Body) throw new Error("Empty response from cold storage");
    const chunks = [];
    const reader = response.Body;
    return new Promise((resolve, reject) => {
      reader.on("data", (chunk) => chunks.push(chunk));
      reader.on("end", () => resolve(Buffer.concat(chunks)));
      reader.on("error", reject);
    });
  }
  async updateLastAccessed(id) {
    const now = /* @__PURE__ */ new Date();
    if (this.pgPool) {
      await this.pgPool.query(
        "UPDATE storage_items SET last_accessed = $1 WHERE id = $2",
        [now, id]
      );
    } else if (this.db) {
      this.db.prepare("UPDATE storage_items SET last_accessed = ? WHERE id = ?").run(now.toISOString(), id);
    }
  }
  startArchivalProcess() {
    setInterval(async () => {
      try {
        await this.archiveOldItems();
      } catch (error) {
        logger.error("Archival process failed", { error });
      }
    }, 6 * 60 * 60 * 1e3);
    logger.info("Started archival process", { intervalHours: 6 });
  }
  /**
   * Get storage statistics
   */
  async getStats() {
    if (!this.isInitialized) await this.initialize();
    let hotItems = 0;
    let coldItems = 0;
    let totalSize = 0;
    if (this.pgPool) {
      const { rows } = await this.pgPool.query(`
        SELECT tier, COUNT(*) as count, SUM(length(data)) as size
        FROM storage_items
        GROUP BY tier
      `);
      for (const row of rows) {
        if (row.tier === "hot" /* HOT */) {
          hotItems = parseInt(row.count);
          totalSize += parseInt(row.size || 0);
        } else {
          coldItems = parseInt(row.count);
        }
      }
    } else if (this.db) {
      const stmt = this.db.prepare(`
        SELECT tier, COUNT(*) as count, SUM(length(data)) as size
        FROM storage_items
        GROUP BY tier
      `);
      const rows = stmt.all();
      for (const row of rows) {
        if (row.tier === "hot" /* HOT */) {
          hotItems = row.count;
          totalSize += row.size || 0;
        } else {
          coldItems = row.count;
        }
      }
    }
    return { hotItems, coldItems, totalSize };
  }
  async close() {
    if (this.pgPool) {
      await this.pgPool.end();
    }
    if (this.db) {
      this.db.close();
    }
    this.isInitialized = false;
    logger.info("Simplified storage closed");
  }
}
var simplified_storage_default = SimplifiedStorage;
export {
  DEFAULT_SIMPLIFIED_CONFIG,
  SimplifiedStorage,
  StorageTier,
  simplified_storage_default as default
};
//# sourceMappingURL=simplified-storage.js.map
