import { getDb } from './db.js';
import { recordMovement } from './movements.js';

export function getPendingInvoice() {
  const db = getDb();
  const invoice = db.prepare(`
    SELECT * FROM invoices WHERE status = 'pending' LIMIT 1
  `).get();
  if (!invoice) return null;
  return { invoice, lines: linesFor(invoice.id) };
}

export function getOrCreatePendingInvoice() {
  const db = getDb();
  const existing = db.prepare(`SELECT * FROM invoices WHERE status = 'pending' LIMIT 1`).get();
  if (existing) return { invoice: existing, lines: linesFor(existing.id) };
  const id = db.prepare(`INSERT INTO invoices (status) VALUES ('pending')`).run().lastInsertRowid;
  const invoice = db.prepare(`SELECT * FROM invoices WHERE id = ?`).get(id);
  return { invoice, lines: [] };
}

export function getInvoice(id) {
  const db = getDb();
  const invoice = db.prepare(`SELECT * FROM invoices WHERE id = ?`).get(Number(id));
  if (!invoice) return null;
  return { invoice, lines: linesFor(invoice.id) };
}

function linesFor(invoiceId) {
  return getDb().prepare(`
    SELECT
      l.*,
      p.sku        AS part_sku,
      p.name_en    AS part_name_en,
      p.name_tg    AS part_name_tg,
      p.quantity_on_hand AS part_on_hand
    FROM invoice_lines l
    LEFT JOIN parts p ON p.id = l.part_id
    WHERE l.invoice_id = ?
    ORDER BY l.sort_order, l.id
  `).all(invoiceId);
}

function nextSortOrder(invoiceId) {
  const row = getDb().prepare(`
    SELECT COALESCE(MAX(sort_order), 0) + 1 AS n FROM invoice_lines WHERE invoice_id = ?
  `).get(invoiceId);
  return row.n;
}

/**
 * Add a parts-based line to an invoice. If a line for the same part_id
 * already exists on this invoice, increments that line's quantity rather
 * than inserting a new row (per-edit merge). The line's unit price is left
 * alone in the merge case so a user-edited price isn't clobbered.
 */
export function addLine({ invoice_id, part_id, quantity, unit_price_dirams }) {
  const db = getDb();
  const invoiceId = Number(invoice_id);
  const partId    = Number(part_id);
  const qty       = Math.floor(Number(quantity));
  if (!invoiceId) throw new Error('invoice_id required');
  if (!partId)    throw new Error('part_id required');
  if (!Number.isInteger(qty) || qty <= 0) throw new Error('quantity must be a positive integer');

  return db.transaction(() => {
    const part = db.prepare(`SELECT id, sale_price FROM parts WHERE id = ?`).get(partId);
    if (!part) throw new Error(`part ${partId} not found`);

    const price = (unit_price_dirams === '' || unit_price_dirams == null)
      ? Number(part.sale_price || 0)
      : Math.round(Number(unit_price_dirams));

    const existing = db.prepare(`
      SELECT id, quantity FROM invoice_lines
      WHERE invoice_id = ? AND part_id = ? AND affects_inventory = 1
      LIMIT 1
    `).get(invoiceId, partId);

    if (existing) {
      db.prepare(`UPDATE invoice_lines SET quantity = ? WHERE id = ?`)
        .run(existing.quantity + qty, existing.id);
      return existing.id;
    }

    return db.prepare(`
      INSERT INTO invoice_lines
        (invoice_id, part_id, label, quantity, unit_price_dirams, affects_inventory, sort_order)
      VALUES (?, ?, NULL, ?, ?, 1, ?)
    `).run(invoiceId, partId, qty, price, nextSortOrder(invoiceId)).lastInsertRowid;
  })();
}

export function addCustomLine({ invoice_id, label, quantity, unit_price_dirams }) {
  const db = getDb();
  const invoiceId = Number(invoice_id);
  const cleanLabel = (label || '').trim();
  const qty   = Math.floor(Number(quantity));
  const price = Math.round(Number(unit_price_dirams || 0));
  if (!invoiceId)  throw new Error('invoice_id required');
  if (!cleanLabel) throw new Error('label required');
  if (!Number.isInteger(qty) || qty <= 0) throw new Error('quantity must be a positive integer');

  return db.prepare(`
    INSERT INTO invoice_lines
      (invoice_id, part_id, label, quantity, unit_price_dirams, affects_inventory, sort_order)
    VALUES (?, NULL, ?, ?, ?, 0, ?)
  `).run(invoiceId, cleanLabel, qty, price, nextSortOrder(invoiceId)).lastInsertRowid;
}

export function updateLine(line_id, { quantity, unit_price_dirams, label }) {
  const db = getDb();
  const id  = Number(line_id);
  if (!id) throw new Error('line_id required');

  const current = db.prepare(`SELECT * FROM invoice_lines WHERE id = ?`).get(id);
  if (!current) throw new Error(`line ${id} not found`);

  const qty   = quantity == null || quantity === ''
    ? current.quantity
    : Math.floor(Number(quantity));
  if (!Number.isInteger(qty) || qty <= 0) throw new Error('quantity must be a positive integer');

  const price = unit_price_dirams == null || unit_price_dirams === ''
    ? current.unit_price_dirams
    : Math.round(Number(unit_price_dirams));

  const newLabel = current.affects_inventory === 0
    ? (label == null ? current.label : String(label).trim() || current.label)
    : current.label;

  db.prepare(`
    UPDATE invoice_lines SET quantity = ?, unit_price_dirams = ?, label = ?
    WHERE id = ?
  `).run(qty, price, newLabel, id);
}

export function removeLine(line_id) {
  getDb().prepare(`DELETE FROM invoice_lines WHERE id = ?`).run(Number(line_id));
}

/**
 * Commit the invoice: for each inventoried line, record an 'out' stock
 * movement (which decrements parts.quantity_on_hand atomically and rejects
 * if there's not enough stock). All movements + the status flip happen in
 * one transaction — if any line fails, nothing is decremented and the
 * draft stays pending.
 */
export function saveInvoice(invoice_id) {
  const db = getDb();
  const id = Number(invoice_id);

  return db.transaction(() => {
    const invoice = db.prepare(`SELECT * FROM invoices WHERE id = ?`).get(id);
    if (!invoice) throw new Error(`invoice ${id} not found`);
    if (invoice.status !== 'pending') throw new Error('invoice already saved');

    const lines = db.prepare(`
      SELECT * FROM invoice_lines WHERE invoice_id = ? ORDER BY sort_order, id
    `).all(id);
    if (lines.length === 0) throw new Error('cannot save empty invoice');

    const reference = `INV-${id}`;
    for (const line of lines) {
      if (line.affects_inventory !== 1) continue;
      recordMovement({
        part_id: line.part_id,
        movement_type: 'out',
        quantity: line.quantity,
        unit_price: line.unit_price_dirams / 100,
        reference
      });
    }

    const total = lines.reduce(
      (sum, l) => sum + (l.quantity * l.unit_price_dirams),
      0
    );

    db.prepare(`
      UPDATE invoices
      SET status = 'saved', saved_at = datetime('now'), total_dirams = ?
      WHERE id = ?
    `).run(total, id);

    return id;
  })();
}

export function cancelInvoice(invoice_id) {
  getDb().prepare(`
    DELETE FROM invoices WHERE id = ? AND status = 'pending'
  `).run(Number(invoice_id));
}
