require("dotenv").config();

const express = require("express");
const axios = require("axios");
const cors = require("cors");
const fs = require("fs");
const path = require("path");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");

const {
  createQris,
  checkQrisStatus,
  cancelQris
} = require("./services/qrisService");

const {
  initTelegramBot,
  sendWithdrawRequest
} = require("./services/telegramService");

const app = express();

app.use(cors());
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString();
  }
}));

const PORT = process.env.PORT || 3000;
const JWT_SECRET = process.env.JWT_SECRET || "secret";
const AUTOGOPAY_API_KEY = process.env.AUTOGOPAY_API_KEY;

const DB_FILE = path.join(__dirname, "database.json");

function loadDB() {
  if (!fs.existsSync(DB_FILE)) {
    fs.writeFileSync(
      DB_FILE,
      JSON.stringify(
        {
          merchants: [],
          transactions: [],
          withdraws: [],
          info_popup: {
            active: false,
            title: "",
            message: "",
            updated_at: null
          }
        },
        null,
        2
      )
    );
  }

  return JSON.parse(fs.readFileSync(DB_FILE));
}

function saveDB(db) {
  fs.writeFileSync(DB_FILE, JSON.stringify(db, null, 2));
}

function now() {
  return new Date().toISOString();
}

function generateApiKey() {
  return `NX_${crypto.randomBytes(24).toString("hex")}`;
}

function generateWithdrawId() {
  return `WD-${Date.now()}`;
}

function rupiah(n) {
  return new Intl.NumberFormat("id-ID", {
    style: "currency",
    currency: "IDR",
    maximumFractionDigits: 0
  }).format(Number(n || 0));
}

function getPlanLimit(plan) {
  if (plan === "pro") return 1000;
  if (plan === "enterprise") return "Unlimited";
  return 50;
}

function getPlanFee(plan) {
  if (plan === "pro") return 3;
  if (plan === "enterprise") return 0.1;
  return 5;
}

function getPlanPrice(plan) {
  if (plan === "pro") return 10000;
  if (plan === "enterprise") return 20000;
  return 0;
}

function todayUsedQRIS(merchantId) {
  const db = loadDB();
  const today = new Date().toISOString().slice(0, 10);

  return db.transactions.filter(t =>
    t.merchant_id === merchantId &&
    String(t.created_at || "").slice(0, 10) === today
  ).length;
}

function mapAgpStatus(status) {
  const s = String(status || "pending").toLowerCase();

  if (s === "settlement") return "PAID";
  if (s === "expire") return "EXPIRED";
  if (s === "cancel") return "CANCEL";

  return "PENDING";
}

function auth(req, res, next) {
  const header = req.headers.authorization || "";
  const token = header.replace("Bearer ", "");

  if (!token) {
    return res.status(401).json({
      success: false,
      message: "Token diperlukan"
    });
  }

  try {
    req.user = jwt.verify(token, JWT_SECRET);
    next();
  } catch {
    return res.status(401).json({
      success: false,
      message: "Token tidak valid"
    });
  }
}

function apiKeyAuth(req, res, next) {
  const apiKey = req.headers["x-api-key"];

  if (!apiKey) {
    return res.status(401).json({
      success: false,
      message: "API key diperlukan"
    });
  }

  const db = loadDB();
  const merchant = db.merchants.find(m => m.api_key === apiKey);

  if (!merchant) {
    return res.status(401).json({
      success: false,
      message: "API key tidak valid"
    });
  }

  req.merchant = merchant;
  next();
}

function creditMerchantIfPaid(db, trx) {
  if (trx.balance_added) return;

  const merchant = db.merchants.find(m => m.id === trx.merchant_id);
  if (!merchant) return;

  merchant.balance += Number(trx.net_amount || 0);
  trx.balance_added = true;
  trx.paid_at = now();
}
// Maintance
app.post("/internal/maintenance", (req, res) => {

  const { secret, mode } = req.body;

  if (secret !== process.env.MAINTENANCE_SECRET) {
    return res.status(403).json({
      success: false
    });
  }

  const lockFile =
    "/var/www/nexpay/nexpay/maintenance.lock";

  if (mode === "on") {
    fs.writeFileSync(lockFile, "on");
  } else {
    fs.rmSync(lockFile, { force: true });
  }

  res.json({
    success: true,
    mode
  });
});
/* AUTH */

app.post("/api/auth/register", async (req, res) => {

  const {
    name,
    email,
    password,
    cfToken
  } = req.body;

  if (!name || !email || !password) {
    return res.status(400).json({
      success: false,
      message: "Nama, email, dan password wajib diisi"
    });
  }

  /* =========================
     VERIFY CAPTCHA
  ========================= */

  try {

    const verify =
      await axios.post(
        "https://challenges.cloudflare.com/turnstile/v0/siteverify",

        new URLSearchParams({
          secret: process.env.TURNSTILE_SECRET_KEY,
          response: cfToken
        }),

        {
          headers: {
            "Content-Type":
              "application/x-www-form-urlencoded"
          }
        }
      );

    if (!verify.data.success) {

      return res.status(400).json({
        success: false,
        message: "Captcha tidak valid"
      });
    }

  } catch (e) {

    return res.status(500).json({
      success: false,
      message: "Gagal verify captcha"
    });
  }

  const db = loadDB();

  if (db.merchants.find(m => m.email === email)) {
    return res.status(400).json({
      success: false,
      message: "Email sudah terdaftar"
    });
  }

  const merchant = {
    id: Date.now(),
    name,
    email,
    password: await bcrypt.hash(password, 10),
    api_key: generateApiKey(),
    balance: 0,
    plan: "starter",
    callback_url: "",
    ip_whitelist: [],
    created_at: now()
  };

  db.merchants.push(merchant);

  saveDB(db);

  const token =
    jwt.sign(
      { id: merchant.id },
      JWT_SECRET
    );

  res.json({
    success: true,
    message: "Register berhasil",
    token,

    merchant: {
      id: merchant.id,
      name: merchant.name,
      email: merchant.email,
      api_key: merchant.api_key,
      balance: merchant.balance,
      plan: merchant.plan
    }
  });
});

app.post("/api/auth/login", async (req, res) => {

  const {
    email,
    password,
    cfToken
  } = req.body;

  /* =========================
     VERIFY CAPTCHA
  ========================= */

  try {

    const verify =
      await axios.post(
        "https://challenges.cloudflare.com/turnstile/v0/siteverify",

        new URLSearchParams({
          secret: process.env.TURNSTILE_SECRET_KEY,
          response: cfToken
        }),

        {
          headers: {
            "Content-Type":
              "application/x-www-form-urlencoded"
          }
        }
      );

    if (!verify.data.success) {

      return res.status(400).json({
        success: false,
        message: "Captcha tidak valid"
      });
    }

  } catch (e) {

    return res.status(500).json({
      success: false,
      message: "Gagal verify captcha"
    });
  }

  const db = loadDB();

  const merchant =
    db.merchants.find(
      m => m.email === email
    );

  if (!merchant) {
    return res.status(401).json({
      success: false,
      message: "Email tidak ditemukan"
    });
  }

  const valid =
    await bcrypt.compare(
      password,
      merchant.password
    );

  if (!valid) {
    return res.status(401).json({
      success: false,
      message: "Password salah"
    });
  }

  const token =
    jwt.sign(
      { id: merchant.id },
      JWT_SECRET
    );

  res.json({
    success: true,
    token,

    merchant: {
      id: merchant.id,
      name: merchant.name,
      email: merchant.email,
      api_key: merchant.api_key,
      balance: merchant.balance,
      plan: merchant.plan
    }
  });
});

/* INFO POPUP (FRONTEND) */

/* INFO POPUP (BOT / INTERNAL) */

app.post("/internal/info-popup", (req, res) => {
  const { secret, active, title, message } = req.body;

  if (secret !== process.env.BOT_INTERNAL_SECRET) {
    return res.status(403).json({
      success: false,
      message: "Forbidden"
    });
  }

  const db = loadDB();

  db.info_popup = {
    active: Boolean(active),
    title: title || "",
    message: message || "",
    updated_at: now()
  };

  saveDB(db);

  res.json({
    success: true,
    info_popup: db.info_popup
  });
});

app.get("/api/info-popup", auth, (req, res) => {
  const db = loadDB();
  const popup = db.info_popup || {};

  res.json({
    success: true,
    active: Boolean(popup.active),
    title: popup.title || "Informasi",
    message: popup.message || ""
  });
});

/* Helper Saldo */
function creditMerchantIfPaid(db, trx) {

  if (trx.balance_added) return;

  const merchant =
    db.merchants.find(
      m => m.id === trx.merchant_id
    );

  if (!merchant) return;

  merchant.balance =
    Number(merchant.balance || 0) +
    Number(trx.net_amount || 0);

  trx.balance_added = true;

  trx.paid_at = new Date().toISOString();
}

/* DASHBOARD */

app.get("/api/merchant/dashboard", auth, async (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  if (!merchant) {
    return res.status(404).json({
      success: false,
      message: "Merchant tidak ditemukan"
    });
  }

  const myTransactions = db.transactions.filter(
    t => t.merchant_id === merchant.id
  );

  for (const trx of myTransactions) {
    if (trx.status === "PENDING") {
      try {
        const agp = await checkQrisStatus(trx.transaction_id);
        const gatewayStatus = agp.data?.transaction_status || trx.gateway_status;

        trx.gateway_status = gatewayStatus;
        trx.status = mapAgpStatus(gatewayStatus);

        if (trx.status === "PAID") {
          creditMerchantIfPaid(db, trx);
        }
      } catch (err) {}
    }
  }

  saveDB(db);

  const transactions = db.transactions
    .filter(t => t.merchant_id === merchant.id)
    .sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

  const success = transactions.filter(t =>
    ["PAID", "SUCCESS", "SETTLEMENT"].includes(t.status)
  );

  const pending = transactions.filter(t => t.status === "PENDING");

  res.json({
    success: true,
    merchant: {
      id: merchant.id,
      name: merchant.name,
      email: merchant.email,
      api_key: merchant.api_key,
      balance: merchant.balance,
      plan: merchant.plan
    },
    stats: {
      total_transactions: transactions.length,
      total_balance: merchant.balance,
      success: success.length,
      pending: pending.length
    },
    qris: {
      today_used: todayUsedQRIS(merchant.id),
      daily_limit: getPlanLimit(merchant.plan)
    },
    transactions
  });
});

/* QRIS WEB */

app.post("/api/web/qris/create", auth, async (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  req.merchant = merchant;
  return createQrisHandler(req, res);
});

app.get("/api/web/qris/status", auth, async (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  req.merchant = merchant;
  return qrisStatusHandler(req, res);
});

app.post("/api/web/qris/cancel", auth, async (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  req.merchant = merchant;
  return qrisCancelHandler(req, res);
});

/* QRIS API KEY */

app.post("/api/qris/create", apiKeyAuth, createQrisHandler);
app.get("/api/qris/status", apiKeyAuth, qrisStatusHandler);
app.post("/api/qris/cancel", apiKeyAuth, qrisCancelHandler);

function randomFeeByPlan(plan) {
  const p = String(plan || "starter").toLowerCase();

  if (p === "enterprise") {
    return Math.floor(Math.random() * 50) + 1;
  }

  if (p === "pro") {
    return Math.floor(Math.random() * 200) + 100;
  }

  return Math.floor(Math.random() * 500) + 500;
}

async function createQrisHandler(req, res) {

  const { amount } = req.body;
  const merchant = req.merchant;

  if (!merchant) {
    return res.status(404).json({
      success: false,
      message: "Merchant tidak ditemukan"
    });
  }

  const originalAmount = Number(amount);

  if (!originalAmount || originalAmount < 1000) {
    return res.status(400).json({
      success: false,
      message: "Minimal nominal QRIS Rp 1.000"
    });
  }

  const used = todayUsedQRIS(merchant.id);
  const limit = getPlanLimit(merchant.plan);

  if (limit !== "Unlimited" && used >= limit) {
    return res.status(429).json({
      success: false,
      message:
        `Limit QRIS harian tercapai. Paket ${merchant.plan} hanya ${limit}x per hari`
    });
  }

  try {

    const feeAmount =
      randomFeeByPlan(merchant.plan);

    const finalAmount =
      originalAmount + feeAmount;

    const agp =
      await createQris(finalAmount);

    if (!agp.success) {
      return res.status(400).json({
        success: false,
        message:
          agp.message || "Gagal generate QRIS"
      });
    }

    const data = agp.data;
    const db = loadDB();

    const totalTrx =
      db.transactions.length + 1;

    const invoiceId =
      `NEXPAY-${String(totalTrx).padStart(3, "0")}`;

    const orderId =
      `NEXPAY-${Date.now()}-${Math.floor(Math.random() * 9000) + 1000}`;

    const trx = {
      id: data.transaction_id,
      transaction_id: data.transaction_id,

      invoice_id: invoiceId,
      order_id: orderId,
      gateway_order_id: data.order_id,

      merchant_id: merchant.id,

      amount: originalAmount,
      gross_amount: finalAmount,

      fee_percent: 0,
      fee_amount: feeAmount,

      net_amount: originalAmount,

      status: mapAgpStatus(data.transaction_status),
      gateway_status: data.transaction_status,

      qr_url: `https://api.nexpay.getaway.biz.id/qris/${invoiceId}.png`,
      gateway_qr_url: data.qr_url,

      qr_string: data.qr_string,

      expired_at: data.expiry_time,
      transaction_time: data.transaction_time,

      created_at: now(),
      paid_at: null,
      balance_added: false
    };

    db.transactions.push(trx);
    saveDB(db);

    res.json({
  success: true,
  message: "QRIS berhasil dibuat",

  transaction: {

    invoice_id:
      trx.invoice_id,

    order_id:
      trx.order_id,

    amount:
      trx.amount,

    gross_amount:
      trx.gross_amount,

    fee_amount:
      trx.fee_amount,

    net_amount:
      trx.net_amount,

    status:
      trx.status,

    qr_url:
      trx.qr_url,

    invoice_url:
      `https://nexpay.getaway.biz.id/invoice?invoice_id=${trx.invoice_id}`,

    qr_image_url:
      `https://api.nexpay.getaway.biz.id/qris/${trx.invoice_id}.png`,

    expired_at:
      trx.expired_at,

    transaction_time:
      trx.transaction_time
  }
});

  } catch (err) {

    res.status(500).json({
      success: false,
      message: "Gagal connect AutoGoPay",
      error: err.response?.data || err.message
    });

  }

}

async function qrisStatusHandler(req, res) {
  const { invoice_id } = req.query;
  const merchant = req.merchant;

  const db = loadDB();

  const trx = db.transactions.find(t =>
    t.invoice_id === invoice_id &&
    t.merchant_id === merchant.id
  );

  if (!trx) {
    return res.status(404).json({
      success: false,
      message: "Invoice tidak ditemukan"
    });
  }

  try {
    const agp = await checkQrisStatus(trx.transaction_id);
    const gatewayStatus = agp.data?.transaction_status || trx.gateway_status;

    trx.gateway_status = gatewayStatus;
    trx.status = mapAgpStatus(gatewayStatus);

    if (trx.status === "PAID") {
      creditMerchantIfPaid(db, trx);
    }

    saveDB(db);

    res.json({
      success: true,
      transaction: trx
    });

  } catch {
    res.json({
      success: true,
      transaction: trx,
      warning: "Gagal refresh status dari AutoGoPay"
    });
  }
}

async function qrisCancelHandler(req, res) {
  const { invoice_id } = req.body;
  const merchant = req.merchant;

  const db = loadDB();

  const trx = db.transactions.find(t =>
    t.invoice_id === invoice_id &&
    t.merchant_id === merchant.id
  );

  if (!trx) {
    return res.status(404).json({
      success: false,
      message: "Invoice tidak ditemukan"
    });
  }

  if (trx.status !== "PENDING") {
    return res.status(400).json({
      success: false,
      message: "QRIS tidak bisa dibatalkan"
    });
  }

  try {
    const agp = await cancelQris(trx.transaction_id);

    if (!agp.success) {
      return res.status(400).json({
        success: false,
        message: agp.message || "Gagal cancel QRIS"
      });
    }

    trx.status = "CANCEL";
    trx.gateway_status = "cancel";

    saveDB(db);

    res.json({
      success: true,
      message: "QRIS berhasil dibatalkan",
      transaction: trx
    });

  } catch (err) {
    res.status(500).json({
      success: false,
      message: "Gagal connect AutoGoPay",
      error: err.response?.data || err.message
    });
  }
}

/* WEBHOOK AUTOGOPAY */

app.post("/api/webhook/autogopay", (req, res) => {
  const signature = req.headers["x-signature"];
  const payload = req.rawBody || JSON.stringify(req.body);

  const expectedSignature = crypto
    .createHmac("sha256", AUTOGOPAY_API_KEY)
    .update(payload)
    .digest("hex");

  if (signature && signature !== expectedSignature) {
    return res.status(401).json({
      success: false,
      message: "Invalid signature"
    });
  }

  const body = req.body;

  if (body.event === "verification.challenge") {
    return res.json({ success: true });
  }

  if (body.event === "transaction.received") {
    const tx = body.transaction;
    const db = loadDB();

    const trx = db.transactions.find(t =>
      t.transaction_id === tx.id ||
      t.id === tx.id
    );

    if (trx) {
      trx.gateway_status = tx.status;
      trx.status = mapAgpStatus(tx.status);

      if (trx.status === "PAID") {
        creditMerchantIfPaid(db, trx);
      }

      saveDB(db);
    }
  }

  res.json({ success: true });
});

/* PROFILE */

app.get("/api/merchant/profile", auth, (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  res.json({
    success: true,
    merchant: {
      id: merchant.id,
      name: merchant.name,
      email: merchant.email,
      api_key: merchant.api_key,
      balance: merchant.balance,
      plan: merchant.plan,
      callback_url: merchant.callback_url,
      ip_whitelist: merchant.ip_whitelist
    }
  });
});

app.put("/api/merchant/profile", auth, (req, res) => {
  const { name, email } = req.body;

  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  merchant.name = name || merchant.name;
  merchant.email = email || merchant.email;

  saveDB(db);

  res.json({
    success: true,
    message: "Profil berhasil disimpan"
  });
});

app.put("/api/merchant/password", auth, async (req, res) => {
  const { old_password, new_password } = req.body;

  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  const valid = await bcrypt.compare(old_password, merchant.password);

  if (!valid) {
    return res.status(400).json({
      success: false,
      message: "Password lama salah"
    });
  }

  merchant.password = await bcrypt.hash(new_password, 10);
  saveDB(db);

  res.json({
    success: true,
    message: "Password berhasil diubah"
  });
});

function getClientIp(req) {
  return (
    req.headers["cf-connecting-ip"] ||
    req.headers["x-forwarded-for"]?.split(",")[0]?.trim() ||
    req.socket.remoteAddress ||
    ""
  ).replace("::ffff:", "");
}

function apiKeyAuth(req, res, next) {

  const apiKey =
    req.headers["x-api-key"];

  if (!apiKey) {
    return res.status(401).json({
      success: false,
      message: "API key diperlukan"
    });
  }

  const db = loadDB();

  const merchant =
    db.merchants.find(
      m => m.api_key === apiKey
    );

  if (!merchant) {
    return res.status(401).json({
      success: false,
      message: "API key tidak valid"
    });
  }

  /* =========================
     CHECK IP WHITELIST
  ========================= */

  const clientIp =
    getClientIp(req);

  const whitelist =
    merchant.ip_whitelist || [];

  // WL kosong = BLOCK
  if (
    !Array.isArray(whitelist) ||
    whitelist.length === 0
  ) {
    return res.status(403).json({
      success: false,
      message: "IP whitelist belum diatur"
    });
  }

  // IP tidak cocok
  if (!whitelist.includes(clientIp)) {
    return res.status(403).json({
      success: false,
      message: "IP tidak diizinkan",
      your_ip: clientIp
    });
  }

  req.merchant = merchant;

  next();
}

app.put("/api/merchant/ip-whitelist", auth, (req, res) => {
  const { ip_whitelist } = req.body;

  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  merchant.ip_whitelist = Array.isArray(ip_whitelist) ? ip_whitelist : [];

  saveDB(db);

  res.json({
    success: true,
    message: "IP whitelist berhasil disimpan"
  });
});

app.put("/api/merchant/callback", auth, (req, res) => {
  const { callback_url } = req.body;

  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  merchant.callback_url = callback_url || "";

  saveDB(db);

  res.json({
    success: true,
    message: "Callback URL berhasil disimpan"
  });
});

app.post("/api/merchant/api-key/regenerate", auth, (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  merchant.api_key = generateApiKey();

  saveDB(db);

  res.json({
    success: true,
    api_key: merchant.api_key
  });
});

/* PRICING */

app.post("/api/merchant/upgrade", auth, (req, res) => {
  const { plan } = req.body;

  if (!["starter", "pro", "enterprise"].includes(plan)) {
    return res.status(400).json({
      success: false,
      message: "Paket tidak valid"
    });
  }

  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  const price = getPlanPrice(plan);

  if (merchant.balance < price) {
    return res.status(400).json({
      success: false,
      message: "Saldo tidak cukup"
    });
  }

  merchant.balance -= price;
  merchant.plan = plan;

  saveDB(db);

  res.json({
    success: true,
    message: "Upgrade berhasil",
    merchant: {
      plan: merchant.plan,
      balance: merchant.balance
    }
  });
});

/* WITHDRAW */

app.get("/api/merchant/withdraw", auth, (req, res) => {
  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  const withdraws = db.withdraws
    .filter(w => w.merchant_id === merchant.id)
    .sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

  res.json({
    success: true,
    balance: merchant.balance,
    withdraws
  });
});

app.post("/api/merchant/withdraw", auth, async (req, res) => {
  const {
    amount,
    method,
    account_name,
    account_number,
    provider
  } = req.body;

  const db = loadDB();
  const merchant = db.merchants.find(m => m.id === req.user.id);

  if (Number(amount) < 10000) {
    return res.status(400).json({
      success: false,
      message: "Minimal withdraw Rp 10.000"
    });
  }

  if (merchant.balance < Number(amount)) {
    return res.status(400).json({
      success: false,
      message: "Saldo tidak cukup"
    });
  }

  merchant.balance -= Number(amount);

  const withdrawId = generateWithdrawId();

  const withdraw = {
    id: withdrawId,
    withdraw_id: withdrawId,
    merchant_id: merchant.id,
    merchant_name: merchant.name,
    merchant_email: merchant.email,
    amount: Number(amount),
    method,
    account_name,
    account_number,
    provider,
    status: "PENDING",
    created_at: now()
  };

  db.withdraws.push(withdraw);
  saveDB(db);

  await sendWithdrawRequest(withdraw, merchant);

  res.json({
    success: true,
    message: "Withdraw berhasil diajukan",
    withdraw
  });
});

function approveWithdraw(withdrawId) {
  const db = loadDB();
  const wd = db.withdraws.find(w => w.withdraw_id === withdrawId);

  if (!wd) {
    return {
      success: false,
      message: "Withdraw tidak ditemukan"
    };
  }

  if (wd.status !== "PENDING") {
    return {
      success: false,
      message: `Withdraw sudah ${wd.status}`
    };
  }

  wd.status = "APPROVED";
  wd.approved_at = now();

  saveDB(db);

  return {
    success: true,
    message: "Withdraw berhasil diapprove"
  };
}

function rejectWithdraw(withdrawId) {
  const db = loadDB();
  const wd = db.withdraws.find(w => w.withdraw_id === withdrawId);

  if (!wd) {
    return {
      success: false,
      message: "Withdraw tidak ditemukan"
    };
  }

  if (wd.status !== "PENDING") {
    return {
      success: false,
      message: `Withdraw sudah ${wd.status}`
    };
  }

  const merchant = db.merchants.find(m => m.id === wd.merchant_id);

  if (merchant) {
    merchant.balance += Number(wd.amount || 0);
  }

  wd.status = "REJECTED";
  wd.rejected_at = now();

  saveDB(db);

  return {
    success: true,
    message: "Withdraw ditolak dan saldo dikembalikan"
  };
}

/* ROOT */

app.get("/", (req, res) => {
  res.json({
    success: true,
    message: "NexPay API aktif"
  });
});

initTelegramBot({
  approveWithdraw,
  rejectWithdraw
});

app.get("/qris/:invoice.png", async (req, res) => {

  const invoiceId =
    req.params.invoice;

  const db = loadDB();

  const trx =
    db.transactions.find(
      t => t.invoice_id === invoiceId
    );

  if (!trx) {
    return res
      .status(404)
      .send("QRIS not found");
  }

  try {

    const response =
      await axios.get(
        trx.gateway_qr_url,
        {
          responseType: "arraybuffer"
        }
      );

    res.set(
      "Content-Type",
      "image/png"
    );

    return res.send(response.data);

  } catch (err) {

    return res
      .status(500)
      .send("Failed load QR");

  }

});

app.listen(PORT, () => {
  console.log(`NexPay API running on port ${PORT}`);
});
