import Database from 'better-sqlite3';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DB_PATH = path.join(__dirname, '..', '..', 'data', 'bot.db');

export const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

export async function initDB() {
  db.exec(`
    -- Bot config per guild (server)
    CREATE TABLE IF NOT EXISTS guilds (
      guild_id TEXT PRIMARY KEY,
      name TEXT,
      owner_id TEXT,
      ticket_category_id TEXT,
      log_channel_id TEXT,
      welcome_channel_id TEXT,
      staff_role_id TEXT,
      currency TEXT DEFAULT 'IDR',
      created_at INTEGER DEFAULT (unixepoch())
    );

    -- Store panels (one big panel = a header image + dropdown of categories)
    CREATE TABLE IF NOT EXISTS store_panels (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      guild_id TEXT NOT NULL,
      title TEXT NOT NULL,
      image_url TEXT,
      footer_note TEXT,
      placeholder TEXT DEFAULT 'Pilih Di Sini',
      copyright TEXT,
      enabled INTEGER DEFAULT 1,
      created_at INTEGER DEFAULT (unixepoch())
    );

    -- Categories (Acc Discord Age, Decoration, Nitro, etc) — what user picks first
    CREATE TABLE IF NOT EXISTS categories (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      guild_id TEXT NOT NULL,
      panel_id INTEGER,
      name TEXT NOT NULL,
      emoji TEXT,
      description TEXT,
      note TEXT,                         -- shown when category opened
      sort_order INTEGER DEFAULT 0,
      enabled INTEGER DEFAULT 1,
      created_at INTEGER DEFAULT (unixepoch()),
      FOREIGN KEY (panel_id) REFERENCES store_panels(id) ON DELETE SET NULL
    );

    -- Products
    CREATE TABLE IF NOT EXISTS products (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      guild_id TEXT NOT NULL,
      category_id INTEGER,
      name TEXT NOT NULL,
      description TEXT,
      price INTEGER NOT NULL,
      price_max INTEGER,                 -- price range "25.000 - 100.000"
      stock_type TEXT DEFAULT 'manual',
      auto_delivery INTEGER DEFAULT 1,
      image_url TEXT,
      enabled INTEGER DEFAULT 1,
      sort_order INTEGER DEFAULT 0,
      created_at INTEGER DEFAULT (unixepoch()),
      FOREIGN KEY (guild_id) REFERENCES guilds(guild_id) ON DELETE CASCADE,
      FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
    );

    CREATE TABLE IF NOT EXISTS stock (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      product_id INTEGER NOT NULL,
      content TEXT NOT NULL,
      sold INTEGER DEFAULT 0,
      sold_to TEXT,
      sold_at INTEGER,
      FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
    );

    CREATE TABLE IF NOT EXISTS orders (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      invoice_id TEXT UNIQUE NOT NULL,
      guild_id TEXT NOT NULL,
      user_id TEXT NOT NULL,
      product_id INTEGER NOT NULL,
      qty INTEGER DEFAULT 1,
      total INTEGER NOT NULL,
      status TEXT DEFAULT 'pending',
      payment_provider TEXT,
      payment_url TEXT,
      paid_at INTEGER,
      delivered_at INTEGER,
      delivered_content TEXT,
      ticket_channel_id TEXT,
      created_at INTEGER DEFAULT (unixepoch()),
      FOREIGN KEY (guild_id) REFERENCES guilds(guild_id),
      FOREIGN KEY (product_id) REFERENCES products(id)
    );

    CREATE TABLE IF NOT EXISTS tickets (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      guild_id TEXT NOT NULL,
      user_id TEXT NOT NULL,
      channel_id TEXT NOT NULL,
      category TEXT,
      product_id INTEGER,
      order_id INTEGER,
      status TEXT DEFAULT 'open',
      created_at INTEGER DEFAULT (unixepoch()),
      closed_at INTEGER,
      transcript_path TEXT,
      FOREIGN KEY (guild_id) REFERENCES guilds(guild_id) ON DELETE CASCADE
    );

    CREATE TABLE IF NOT EXISTS clone_snapshots (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      source_guild_id TEXT NOT NULL,
      created_by TEXT NOT NULL,
      data TEXT NOT NULL,
      created_at INTEGER DEFAULT (unixepoch())
    );

    CREATE TABLE IF NOT EXISTS audit_log (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      guild_id TEXT,
      user_id TEXT,
      action TEXT,
      data TEXT,
      created_at INTEGER DEFAULT (unixepoch())
    );

    -- ===== Multi-tenant / commercial =====

    -- Dashboard accounts (admin = kamu, user = pelanggan)
    CREATE TABLE IF NOT EXISTS users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      username TEXT UNIQUE NOT NULL,
      password_hash TEXT NOT NULL,
      role TEXT NOT NULL DEFAULT 'user',   -- 'admin' | 'user'
      enabled INTEGER DEFAULT 1,
      created_at INTEGER DEFAULT (unixepoch())
    );

    -- Login sessions (opaque token disimpan di sini)
    CREATE TABLE IF NOT EXISTS sessions (
      token TEXT PRIMARY KEY,
      user_id INTEGER NOT NULL,
      created_at INTEGER DEFAULT (unixepoch()),
      expires_at INTEGER NOT NULL,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    );

    -- Bot instance per user (token bot DISIMPAN TERENKRIPSI di token_encrypted)
    CREATE TABLE IF NOT EXISTS bot_instances (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      bot_name TEXT,
      client_id TEXT,
      token_encrypted TEXT,
      status TEXT DEFAULT 'stopped',       -- stopped | running | error
      last_error TEXT,
      created_at INTEGER DEFAULT (unixepoch()),
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    );

    -- Konfigurasi pembayaran per-guild (key gateway DISIMPAN TERENKRIPSI)
    CREATE TABLE IF NOT EXISTS payment_configs (
      guild_id TEXT PRIMARY KEY,
      -- Gateway otomatis
      tripay_enabled INTEGER DEFAULT 0,
      tripay_api_key_enc TEXT,
      tripay_private_key_enc TEXT,
      tripay_merchant_code TEXT,
      midtrans_enabled INTEGER DEFAULT 0,
      midtrans_server_key_enc TEXT,
      midtrans_client_key TEXT,
      midtrans_is_production INTEGER DEFAULT 0,
      auto_delivery INTEGER DEFAULT 1,
      -- Pembayaran manual (dipakai kalau semua gateway OFF)
      manual_methods TEXT,          -- teks bebas: "DANA 0812.. / BCA 123.. a.n X"
      qris_image_url TEXT,
      updated_at INTEGER DEFAULT (unixepoch()),
      FOREIGN KEY (guild_id) REFERENCES guilds(guild_id) ON DELETE CASCADE
    );

    CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
    CREATE INDEX IF NOT EXISTS idx_bot_instances_user ON bot_instances(user_id);

    CREATE INDEX IF NOT EXISTS idx_orders_user ON orders(user_id);
    CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
    CREATE INDEX IF NOT EXISTS idx_stock_product ON stock(product_id, sold);
    CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
    CREATE INDEX IF NOT EXISTS idx_products_cat ON products(category_id, enabled);
    CREATE INDEX IF NOT EXISTS idx_categories_panel ON categories(panel_id, enabled);
  `);
  console.log('✅ Database initialized');

  // ---- Migrations (aman buat DB lama) ----
  migrate();
}

// Tambah kolom yang belum ada tanpa ngerusak data lama
function migrate() {
  const cols = db.prepare('PRAGMA table_info(guilds)').all().map(c => c.name);
  if (!cols.includes('owner_user_id')) {
    db.exec('ALTER TABLE guilds ADD COLUMN owner_user_id INTEGER');
    console.log('  ↳ migrate: guilds.owner_user_id added');
  }
  db.exec('CREATE INDEX IF NOT EXISTS idx_guilds_owner ON guilds(owner_user_id)');

  const em = ['emoji_price','emoji_stock','emoji_sku','emoji_category','emoji_description','emoji_order','ticket_color'];
  for (const c of em) {
    if (!cols.includes(c)) {
      db.exec(`ALTER TABLE guilds ADD COLUMN ${c} TEXT`);
    }
  }

  const payCols = db.prepare('PRAGMA table_info(payment_configs)').all().map(c => c.name);
  const payNew = ['pakasir_enabled','pakasir_slug','pakasir_api_key_enc'];
  for (const c of payNew) {
    if (!payCols.includes(c)) {
      const def = c === 'pakasir_enabled' ? 'INTEGER DEFAULT 0' : 'TEXT';
      db.exec(`ALTER TABLE payment_configs ADD COLUMN ${c} ${def}`);
      console.log(`  ↳ migrate: payment_configs.${c} added`);
    }
  }
}

export function audit(guildId, userId, action, data = {}) {
  db.prepare('INSERT INTO audit_log (guild_id, user_id, action, data) VALUES (?, ?, ?, ?)')
    .run(guildId, userId, action, JSON.stringify(data));
}

export function getGuildConfig(guildId) {
  let row = db.prepare('SELECT * FROM guilds WHERE guild_id = ?').get(guildId);
  if (!row) {
    db.prepare('INSERT INTO guilds (guild_id) VALUES (?)').run(guildId);
    row = db.prepare('SELECT * FROM guilds WHERE guild_id = ?').get(guildId);
  }
  return row;
}

export function updateGuildConfig(guildId, fields) {
  const keys = Object.keys(fields);
  if (!keys.length) return;
  const sets = keys.map(k => `${k} = ?`).join(', ');
  db.prepare(`UPDATE guilds SET ${sets} WHERE guild_id = ?`).run(...keys.map(k => fields[k]), guildId);
}

// ==================================================================================================
// User / auth helpers
// ==================================================================================================

export function getUserByUsername(username) {
  return db.prepare('SELECT * FROM users WHERE username = ?').get(username);
}

export function getUserById(id) {
  return db.prepare('SELECT * FROM users WHERE id = ?').get(id);
}

export function createUser({ username, passwordHash, role = 'user' }) {
  const r = db.prepare(
    'INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)'
  ).run(username, passwordHash, role);
  return r.lastInsertRowid;
}

export function listUsers() {
  return db.prepare('SELECT id, username, role, enabled, created_at FROM users ORDER BY id').all();
}

export function setUserEnabled(id, enabled) {
  db.prepare('UPDATE users SET enabled = ? WHERE id = ?').run(enabled ? 1 : 0, id);
}

export function updateUserPassword(id, passwordHash) {
  db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(passwordHash, id);
}

export function deleteUser(id) {
  db.prepare('DELETE FROM users WHERE id = ?').run(id);
}

// ==================================================================================================
// Session helpers
// ==================================================================================================

export function createSession(token, userId, ttlSeconds = 7 * 24 * 3600) {
  const expires = Math.floor(Date.now() / 1000) + ttlSeconds;
  db.prepare('INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)')
    .run(token, userId, expires);
  return expires;
}

export function getSession(token) {
  if (!token) return null;
  const row = db.prepare('SELECT * FROM sessions WHERE token = ?').get(token);
  if (!row) return null;
  if (row.expires_at < Math.floor(Date.now() / 1000)) {
    db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
    return null;
  }
  return row;
}

export function deleteSession(token) {
  db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
}

export function cleanupExpiredSessions() {
  db.prepare('DELETE FROM sessions WHERE expires_at < ?').run(Math.floor(Date.now() / 1000));
}

// ==================================================================================================
// Bot instance helpers (per-user bots)
// ==================================================================================================

export function getBotInstanceByUser(userId) {
  return db.prepare('SELECT * FROM bot_instances WHERE user_id = ?').get(userId);
}

export function upsertBotInstance(userId, fields) {
  const existing = getBotInstanceByUser(userId);
  if (existing) {
    const keys = Object.keys(fields);
    if (!keys.length) return existing.id;
    const sets = keys.map(k => `${k} = ?`).join(', ');
    db.prepare(`UPDATE bot_instances SET ${sets} WHERE user_id = ?`)
      .run(...keys.map(k => fields[k]), userId);
    return existing.id;
  }
  const cols = ['user_id', ...Object.keys(fields)];
  const placeholders = cols.map(() => '?').join(', ');
  const r = db.prepare(`INSERT INTO bot_instances (${cols.join(', ')}) VALUES (${placeholders})`)
    .run(userId, ...Object.keys(fields).map(k => fields[k]));
  return r.lastInsertRowid;
}

export function listBotInstances() {
  return db.prepare('SELECT * FROM bot_instances').all();
}

// ==================================================================================================
// Payment config helpers (per-guild). Kolom *_enc disimpan apa adanya (sudah terenkripsi
// oleh caller). Enkripsi/dekripsi ditangani di payment layer, bukan di sini.
// ==================================================================================================

export function getPaymentConfig(guildId) {
  return db.prepare('SELECT * FROM payment_configs WHERE guild_id = ?').get(guildId);
}

export function upsertPaymentConfig(guildId, fields) {
  const existing = getPaymentConfig(guildId);
  if (existing) {
    const keys = Object.keys(fields);
    if (!keys.length) return;
    const sets = keys.map(k => `${k} = ?`).join(', ');
    db.prepare(`UPDATE payment_configs SET ${sets}, updated_at = unixepoch() WHERE guild_id = ?`)
      .run(...keys.map(k => fields[k]), guildId);
    return;
  }
  const cols = ['guild_id', ...Object.keys(fields)];
  const placeholders = cols.map(() => '?').join(', ');
  db.prepare(`INSERT INTO payment_configs (${cols.join(', ')}) VALUES (${placeholders})`)
    .run(guildId, ...Object.keys(fields).map(k => fields[k]));
}

// ==================================================================================================
// Guild ownership (isolasi data per-user)
// ==================================================================================================

// Tag sebuah guild sebagai milik user (dipanggil saat bot user join/ready di guild itu).
// Tidak menimpa owner yang sudah ada kecuali sebelumnya null (hindari rebут kepemilikan).
export function claimGuildOwner(guildId, userId) {
  const row = db.prepare('SELECT owner_user_id FROM guilds WHERE guild_id = ?').get(guildId);
  if (!row) {
    db.prepare('INSERT INTO guilds (guild_id, owner_user_id) VALUES (?, ?)').run(guildId, userId);
    return;
  }
  if (row.owner_user_id == null) {
    db.prepare('UPDATE guilds SET owner_user_id = ? WHERE guild_id = ?').run(userId, guildId);
  }
}

// Daftar guild_id yang dimiliki user
export function getOwnedGuildIds(userId) {
  return db.prepare('SELECT guild_id FROM guilds WHERE owner_user_id = ?').all(userId).map(r => r.guild_id);
}

// Apakah user pemilik guild ini?
export function userOwnsGuild(userId, guildId) {
  const row = db.prepare('SELECT owner_user_id FROM guilds WHERE guild_id = ?').get(guildId);
  return !!row && row.owner_user_id === userId;
}
