/**
 * Rollback test: force MySQL dual-write failure and assert Mongo compensates.
 */
import fs from "fs";
import path from "path";
import mongoose from "mongoose";
import mysql from "mysql2/promise";
import { creditWallet } from "../src/services/wallet";
import { postBookingPaymentLedger } from "../src/services/ledger";
import { connectMysql, disconnectMysql } from "../src/db/mysql/pool";
import { createId } from "../src/db/ids";

function loadEnv() {
  const t = fs.readFileSync(path.join(process.cwd(), ".env"), "utf8");
  for (const line of t.split(/\r?\n/)) {
    const s = line.trim();
    if (!s || s.startsWith("#")) continue;
    const eq = s.indexOf("=");
    if (eq <= 0) continue;
    const k = s.slice(0, eq).trim();
    let v = s.slice(eq + 1).trim();
    if (
      (v.startsWith('"') && v.endsWith('"')) ||
      (v.startsWith("'") && v.endsWith("'"))
    )
      v = v.slice(1, -1);
    process.env[k] = v;
  }
}

async function main() {
  loadEnv();
  process.env.DUAL_WRITE = "true";
  process.env.ACTIVE_DATABASE = "mongodb";
  process.env.DUAL_WRITE_FINANCIAL_FAIL = "1";

  await mongoose.connect(process.env.MONGODB_URI!);
  await connectMysql();

  const results: { name: string; pass: boolean; detail: string }[] = [];
  const check = (name: string, pass: boolean, detail: string) => {
    results.push({ name, pass, detail });
    console.log(`${pass ? "PASS" : "FAIL"}  ${name} — ${detail}`);
  };

  const user = await mongoose.connection.db!.collection("users").findOne({
    role: "CUSTOMER",
    deletedAt: null,
  });
  if (!user) throw new Error("no customer");
  const userId = String(user._id);

  const walletBefore = await mongoose.connection.db!.collection("wallets").findOne({
    userId: userId as any,
  });
  const balBefore = Number(walletBefore?.balanceLyd || 0);
  const txnCountBefore = await mongoose.connection
    .db!.collection("wallettxns")
    .countDocuments({ userId });

  let creditThrew = false;
  try {
    await creditWallet({
      userId,
      amountLyd: 1.23,
      type: "ADMIN_ADJUST",
      note: "rollback selftest",
    });
  } catch {
    creditThrew = true;
  }
  check("credit_throws_on_mysql_fail", creditThrew, `threw=${creditThrew}`);

  const walletAfter = await mongoose.connection.db!.collection("wallets").findOne({
    userId: userId as any,
  });
  const balAfter = Number(walletAfter?.balanceLyd || 0);
  const txnCountAfter = await mongoose.connection
    .db!.collection("wallettxns")
    .countDocuments({ userId });
  check(
    "wallet_balance_unchanged",
    Math.abs(balAfter - balBefore) < 0.001,
    `before=${balBefore} after=${balAfter}`,
  );
  check(
    "wallet_txn_not_left_behind",
    txnCountAfter === txnCountBefore,
    `before=${txnCountBefore} after=${txnCountAfter}`,
  );

  const pool = await mysql.createPool({
    host: process.env.MYSQL_HOST,
    port: Number(process.env.MYSQL_PORT),
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    database: process.env.MYSQL_DATABASE,
  });
  const [mysqlTxns] = await pool.query(
    `SELECT id FROM wallet_txns WHERE note = ?`,
    ["rollback selftest"],
  );
  check(
    "mysql_no_orphan_txn",
    (mysqlTxns as any[]).length === 0,
    `count=${(mysqlTxns as any[]).length}`,
  );

  // Ledger post rollback
  const fakeBookingId = createId();
  const ledgerCountBefore = await mongoose.connection
    .db!.collection("ledgerentries")
    .countDocuments({});
  let ledgerThrew = false;
  try {
    await postBookingPaymentLedger({
      _id: fakeBookingId,
      customerId: userId,
      totalLyd: 10,
      totalTnd: 7,
      platformFeeTnd: 1,
      ownerPayoutTnd: 5,
      exchangeRateRate: 1.5,
    });
  } catch {
    ledgerThrew = true;
  }
  check("ledger_throws_on_mysql_fail", ledgerThrew, `threw=${ledgerThrew}`);
  const ledgerCountAfter = await mongoose.connection
    .db!.collection("ledgerentries")
    .countDocuments({});
  check(
    "ledger_not_left_in_mongo",
    ledgerCountAfter === ledgerCountBefore,
    `before=${ledgerCountBefore} after=${ledgerCountAfter}`,
  );
  const [mysqlLedgers] = await pool.query(
    `SELECT id FROM ledger_entries WHERE booking_id = ?`,
    [fakeBookingId],
  );
  check(
    "mysql_no_orphan_ledger",
    (mysqlLedgers as any[]).length === 0,
    `count=${(mysqlLedgers as any[]).length}`,
  );

  delete process.env.DUAL_WRITE_FINANCIAL_FAIL;
  const passed = results.filter((r) => r.pass).length;
  console.log(`\nSUMMARY dualwrite-rollback: ${passed}/${results.length} pass`);
  await pool.end();
  await disconnectMysql();
  await mongoose.disconnect();
  process.exit(passed === results.length ? 0 : 1);
}

main().catch(async (e) => {
  console.error(e instanceof Error ? e.message : e);
  delete process.env.DUAL_WRITE_FINANCIAL_FAIL;
  try {
    await disconnectMysql();
  } catch {
    /* ignore */
  }
  try {
    await mongoose.disconnect();
  } catch {
    /* ignore */
  }
  process.exit(1);
});
