/* ═══════════════════════════════════════════════════════
   KLING MOTION TEKNO — Finance Store
   Transaction logging, reporting, balance tracking
   ═══════════════════════════════════════════════════════ */

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const FINANCE_FILE = path.join(__dirname, "..", "data", "finance.json");

function ensureDir() {
  const dir = path.dirname(FINANCE_FILE);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

function readAll() {
  ensureDir();
  try {
    if (fs.existsSync(FINANCE_FILE)) {
      const raw = fs.readFileSync(FINANCE_FILE, "utf8");
      return JSON.parse(raw);
    }
  } catch (e) {
    console.error("[Finance] Failed to read finance data:", e.message);
  }
  return { transactions: [], balance: 0 };
}

function writeAll(data) {
  ensureDir();
  try {
    fs.writeFileSync(FINANCE_FILE, JSON.stringify(data, null, 2), "utf8");
  } catch (e) {
    console.error("[Finance] Failed to write finance data:", e.message);
  }
}

// ═══════════════════════════════════════════════════════
// TRANSACTION LOGGING
// ═══════════════════════════════════════════════════════

function getWeekString(date = new Date()) {
  const d = new Date(date);
  const dayNum = d.getUTCDay() || 7;
  d.setUTCDate(d.getUTCDate() + 4 - dayNum);
  const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
  const weekNo = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
  return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
}

function getDayString(date = new Date()) {
  return new Date(date).toISOString().split("T")[0];
}

function getMonthString(date = new Date()) {
  const d = new Date(date);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
}

/**
 * Add a financial transaction to the log
 * @param {Object} opts
 * @param {"topup"|"voucher_sale"|"commission"|"adjustment"|"refund"|"debt"} opts.type
 * @param {"owner"|"kasir"|string} opts.actor - "owner", "kasir", or "mitra_{id}"
 * @param {string} opts.actorName
 * @param {number} opts.amount - positive = income/masuk, negative = expense/keluar
 * @param {string} opts.description
 * @param {string} [opts.voucherCode]
 * @param {string} [opts.relatedResellerId]
 * @param {"pro"|"vip"} [opts.category]
 * @param {Object} [opts.meta] - additional metadata
 */
export function addTransaction(opts) {
  const data = readAll();
  const now = new Date();
  const txn = {
    id: "txn_" + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 8),
    type: opts.type,
    actor: opts.actor,
    actorName: opts.actorName,
    amount: opts.amount,
    description: opts.description,
    voucherCode: opts.voucherCode || null,
    relatedResellerId: opts.relatedResellerId || null,
    category: opts.category || null,
    meta: opts.meta || null,
    timestamp: now.toISOString(),
    day: getDayString(now),
    week: getWeekString(now),
    month: getMonthString(now),
  };
  data.transactions.unshift(txn);

  // Update running balance
  data.balance = (data.balance || 0) + opts.amount;

  // Limit log to last 10,000 entries
  if (data.transactions.length > 10000) {
    data.transactions = data.transactions.slice(0, 10000);
  }

  writeAll(data);
  return txn;
}

// ═══════════════════════════════════════════════════════
// QUERIES & REPORTS
// ═══════════════════════════════════════════════════════

/**
 * Get financial summary (today, week, month, total)
 */
export function getSummary() {
  const data = readAll();
  const today = getDayString();
  const week = getWeekString();
  const month = getMonthString();

  let todayIncome = 0;
  let weekIncome = 0;
  let monthIncome = 0;
  let totalIncome = 0;
  let todayExpense = 0;
  let weekExpense = 0;
  let monthExpense = 0;
  let totalExpense = 0;

  // Per-reseller breakdown
  const perReseller = {};

  for (const t of data.transactions) {
    const amt = t.amount || 0;
    if (amt >= 0) {
      totalIncome += amt;
      if (t.day === today) todayIncome += amt;
      if (t.week === week) weekIncome += amt;
      if (t.month === month) monthIncome += amt;
    } else {
      totalExpense += Math.abs(amt);
      if (t.day === today) todayExpense += Math.abs(amt);
      if (t.week === week) weekExpense += Math.abs(amt);
      if (t.month === month) monthExpense += Math.abs(amt);
    }

    // Per-reseller tracking
    if (t.relatedResellerId) {
      if (!perReseller[t.relatedResellerId]) {
        perReseller[t.relatedResellerId] = {
          resellerId: t.relatedResellerId,
          resellerName: t.actorName,
          totalIncome: 0,
          totalExpense: 0,
          voucherCount: 0,
        };
      }
      if (amt >= 0) {
        perReseller[t.relatedResellerId].totalIncome += amt;
      } else {
        perReseller[t.relatedResellerId].totalExpense += Math.abs(amt);
      }
      if (t.type === "voucher_sale") {
        perReseller[t.relatedResellerId].voucherCount++;
      }
    }
  }

  // Per-category breakdown
  const perCategory = {};
  for (const t of data.transactions) {
    if (t.category && t.type === "voucher_sale") {
      if (!perCategory[t.category]) {
        perCategory[t.category] = { count: 0, totalAmount: 0 };
      }
      perCategory[t.category].count++;
      perCategory[t.category].totalAmount += Math.max(0, t.amount || 0);
    }
  }

  return {
    today: { income: todayIncome, expense: todayExpense, net: todayIncome - todayExpense },
    week: { income: weekIncome, expense: weekExpense, net: weekIncome - weekExpense },
    month: { income: monthIncome, expense: monthExpense, net: monthIncome - monthExpense },
    total: { income: totalIncome, expense: totalExpense, net: totalIncome - totalExpense },
    runningBalance: data.balance || 0,
    perReseller: Object.values(perReseller),
    perCategory,
    transactionCount: data.transactions.length,
  };
}

/**
 * Get transactions with optional filters
 */
export function getTransactions(filters = {}) {
  const data = readAll();
  let results = [...data.transactions];

  if (filters.type) {
    results = results.filter((t) => t.type === filters.type);
  }
  if (filters.actor) {
    results = results.filter((t) => t.actor === filters.actor);
  }
  if (filters.relatedResellerId) {
    results = results.filter((t) => t.relatedResellerId === filters.relatedResellerId);
  }
  if (filters.category) {
    results = results.filter((t) => t.category === filters.category);
  }
  if (filters.day) {
    results = results.filter((t) => t.day === filters.day);
  }
  if (filters.week) {
    results = results.filter((t) => t.week === filters.week);
  }
  if (filters.month) {
    results = results.filter((t) => t.month === filters.month);
  }
  if (filters.from) {
    results = results.filter((t) => t.timestamp >= filters.from);
  }
  if (filters.to) {
    results = results.filter((t) => t.timestamp <= filters.to);
  }
  if (filters.search) {
    const q = filters.search.toLowerCase();
    results = results.filter(
      (t) =>
        t.description.toLowerCase().includes(q) ||
        (t.voucherCode && t.voucherCode.toLowerCase().includes(q)) ||
        t.actorName.toLowerCase().includes(q)
    );
  }

  const totalAmount = results.reduce((sum, t) => sum + (t.amount || 0), 0);
  const incomeAmount = results.filter((t) => t.amount > 0).reduce((sum, t) => sum + t.amount, 0);
  const expenseAmount = results.filter((t) => t.amount < 0).reduce((sum, t) => sum + Math.abs(t.amount), 0);

  return {
    transactions: results.slice(0, filters.limit || 500),
    total: results.length,
    totalAmount,
    incomeAmount,
    expenseAmount,
  };
}

/**
 * Get daily report for a specific date
 */
export function getDailyReport(day) {
  const targetDay = day || getDayString();
  const data = readAll();
  const dayTxns = data.transactions.filter((t) => t.day === targetDay);

  let income = 0;
  let expense = 0;
  const byType = {};
  const byCategory = {};

  for (const t of dayTxns) {
    if (t.amount >= 0) income += t.amount;
    else expense += Math.abs(t.amount);

    if (!byType[t.type]) byType[t.type] = { count: 0, amount: 0 };
    byType[t.type].count++;
    byType[t.type].amount += t.amount;

    if (t.category) {
      if (!byCategory[t.category]) byCategory[t.category] = { count: 0, amount: 0 };
      byCategory[t.category].count++;
      byCategory[t.category].amount += Math.max(0, t.amount);
    }
  }

  return {
    day: targetDay,
    income,
    expense,
    net: income - expense,
    transactionCount: dayTxns.length,
    byType,
    byCategory,
    transactions: dayTxns,
  };
}

/**
 * Get weekly report
 */
export function getWeeklyReport(week) {
  const targetWeek = week || getWeekString();
  const data = readAll();
  const weekTxns = data.transactions.filter((t) => t.week === targetWeek);

  let income = 0;
  let expense = 0;
  const byDay = {};
  const byType = {};

  for (const t of weekTxns) {
    if (t.amount >= 0) income += t.amount;
    else expense += Math.abs(t.amount);

    if (!byDay[t.day]) byDay[t.day] = { income: 0, expense: 0, count: 0 };
    if (t.amount >= 0) byDay[t.day].income += t.amount;
    else byDay[t.day].expense += Math.abs(t.amount);
    byDay[t.day].count++;

    if (!byType[t.type]) byType[t.type] = { count: 0, amount: 0 };
    byType[t.type].count++;
    byType[t.type].amount += t.amount;
  }

  return {
    week: targetWeek,
    income,
    expense,
    net: income - expense,
    transactionCount: weekTxns.length,
    byDay,
    byType,
    transactions: weekTxns,
  };
}

/**
 * Get monthly report
 */
export function getMonthlyReport(month) {
  const targetMonth = month || getMonthString();
  const data = readAll();
  const monthTxns = data.transactions.filter((t) => t.month === targetMonth);

  let income = 0;
  let expense = 0;
  const byWeek = {};
  const byType = {};
  const byCategory = {};

  for (const t of monthTxns) {
    if (t.amount >= 0) income += t.amount;
    else expense += Math.abs(t.amount);

    if (!byWeek[t.week]) byWeek[t.week] = { income: 0, expense: 0, count: 0 };
    if (t.amount >= 0) byWeek[t.week].income += t.amount;
    else byWeek[t.week].expense += Math.abs(t.amount);
    byWeek[t.week].count++;

    if (!byType[t.type]) byType[t.type] = { count: 0, amount: 0 };
    byType[t.type].count++;
    byType[t.type].amount += t.amount;

    if (t.category) {
      if (!byCategory[t.category]) byCategory[t.category] = { count: 0, amount: 0 };
      byCategory[t.category].count++;
      byCategory[t.category].amount += Math.max(0, t.amount);
    }
  }

  return {
    month: targetMonth,
    income,
    expense,
    net: income - expense,
    transactionCount: monthTxns.length,
    byWeek,
    byType,
    byCategory,
    transactions: monthTxns,
  };
}

/**
 * Get transaction log (for activity tracking)
 */
export function getLogs(filters = {}) {
  return getTransactions(filters);
}

/**
 * Get reseller financial summary
 */
export function getResellerSummary(resellerId) {
  const data = readAll();
  const resellerTxns = data.transactions.filter((t) => t.relatedResellerId === resellerId);

  let totalIncome = 0;
  let totalExpense = 0;
  let voucherCount = 0;
  const byMonth = {};

  for (const t of resellerTxns) {
    if (t.amount >= 0) totalIncome += t.amount;
    else totalExpense += Math.abs(t.amount);
    if (t.type === "voucher_sale") voucherCount++;

    if (!byMonth[t.month]) byMonth[t.month] = { income: 0, expense: 0, count: 0 };
    if (t.amount >= 0) byMonth[t.month].income += t.amount;
    else byMonth[t.month].expense += Math.abs(t.amount);
    byMonth[t.month].count++;
  }

  return {
    resellerId,
    totalIncome,
    totalExpense,
    net: totalIncome - totalExpense,
    voucherCount,
    transactionCount: resellerTxns.length,
    byMonth,
    recentTransactions: resellerTxns.slice(0, 50),
  };
}

// ═══════════════════════════════════════════════════════
// UTILITY
// ═══════════════════════════════════════════════════════

export function formatCurrency(amount) {
  return "Rp " + Math.abs(amount).toLocaleString("id-ID");
}

export function getAvailableMonths() {
  const data = readAll();
  const months = new Set();
  for (const t of data.transactions) {
    if (t.month) months.add(t.month);
  }
  return [...months].sort().reverse();
}
