const pool = require('../../database/database');
const Invoice = require('../../models/Invoice');

async function genInvoiceNumber() {
  const year = new Date().getFullYear();
  const prefix = `RE-${year}-`;
  const [rows] = await pool.query(
    `SELECT invoice_number FROM invoices
     WHERE invoice_number LIKE ?
     ORDER BY CAST(SUBSTRING_INDEX(invoice_number, '-', -1) AS UNSIGNED) DESC
     LIMIT 1`,
    [`${prefix}%`],
  );
  let next = 1;
  if (rows.length) {
    const last = rows[0].invoice_number; // e.g. RE-2026-01032026
    const num = parseInt(last.split('-').pop(), 10);
    if (!isNaN(num)) next = num + 1;
  }
  return `${prefix}${String(next).padStart(8, '0')}`;
}

class InvoiceRepository {
  async createInvoiceWithItems({
    source_type,
    source_id,
    user_id,
    buyer_name,
    buyer_email,
    buyer_street,
    buyer_postal_code,
    buyer_city,
    buyer_country,
    currency,
    items = [],
    status = 'draft',
    issued_at = null,
    due_at = null,
    context = null,
    vat_rate = null, // NEW: default VAT for invoice and lines
  }) {
    const conn = await pool.getConnection();
    try {
      await conn.beginTransaction();
      const invoice_number = await genInvoiceNumber();

      // compute totals
      let total_net = 0;
      let total_tax = 0;
      let total_gross = 0;
      for (const it of items) {
        const qty = Number(it.quantity || 1);
        const unit = Number(it.unit_price || 0);
        const rate = it.tax_rate != null ? Number(it.tax_rate) : (vat_rate != null ? Number(vat_rate) : null); // CHANGED
        const line_net = qty * unit;
        const line_tax = rate != null ? +(line_net * (rate / 100)).toFixed(2) : 0;
        const line_gross = +(line_net + line_tax).toFixed(2);
        total_net += line_net;
        total_tax += line_tax;
        total_gross += line_gross;
      }
      total_net = +total_net.toFixed(2);
      total_tax = +total_tax.toFixed(2);
      total_gross = +total_gross.toFixed(2);

      const [res] = await conn.query(
        `INSERT INTO invoices
         (invoice_number, user_id, source_type, source_id, buyer_name, buyer_email, buyer_street, buyer_postal_code, buyer_city, buyer_country,
          currency, total_net, total_tax, total_gross, vat_rate, status, issued_at, due_at, pdf_storage_key, context, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, NOW(), NOW())`,
        [
          invoice_number,
          user_id || null,
          source_type,
          source_id,
          buyer_name || null,
          buyer_email || null,
          buyer_street || null,
          buyer_postal_code || null,
          buyer_city || null,
          buyer_country || null,
          currency,
          total_net,
          total_tax,
          total_gross,
          vat_rate != null ? Number(vat_rate) : null, // CHANGED
          status,
          issued_at || null,
          due_at || null,
          context ? JSON.stringify(context) : null,
        ],
      );

      const invoiceId = res.insertId;

      for (const it of items) {
        const qty = Number(it.quantity || 1);
        const unit = Number(it.unit_price || 0);
        const rate = it.tax_rate != null ? Number(it.tax_rate) : (vat_rate != null ? Number(vat_rate) : null); // CHANGED
        const line_net = +(qty * unit).toFixed(2);
        const line_tax = rate != null ? +(line_net * (rate / 100)).toFixed(2) : 0;
        const line_gross = +(line_net + line_tax).toFixed(2);

        await conn.query(
          `INSERT INTO invoice_items
           (invoice_id, product_id, sku, description, quantity, unit_price, tax_rate, line_net, line_tax, line_gross, created_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
          [
            invoiceId,
            it.product_id || null,
            it.sku || null,
            it.description || 'Subscription',
            qty,
            unit,
            rate, // CHANGED
            line_net,
            line_tax,
            line_gross,
          ],
        );
      }

      await conn.commit();
      return this.getById(invoiceId);
    } catch (e) {
      await conn.rollback();
      throw e;
    } finally {
      conn.release();
    }
  }

  async markPaid(invoiceId, { payment_method, transaction_id, amount, paid_at, details }) {
    const conn = await pool.getConnection();
    try {
      await conn.beginTransaction();
      await conn.query(`UPDATE invoices SET status='paid', updated_at=NOW() WHERE id = ?`, [invoiceId]);
      await conn.query(
        `INSERT INTO invoice_payments
         (invoice_id, payment_method, transaction_id, amount, paid_at, status, details, created_at)
         VALUES (?, ?, ?, ?, ?, 'succeeded', ?, NOW())`,
        [invoiceId, payment_method || 'manual', transaction_id || null, amount || null, paid_at || new Date(), details ? JSON.stringify(details) : null],
      );
      await conn.commit();
      return this.getById(invoiceId);
    } catch (e) {
      await conn.rollback();
      throw e;
    } finally {
      conn.release();
    }
  }

  async getById(id) {
    const [rows] = await pool.query(`SELECT * FROM invoices WHERE id = ?`, [id]);
    return rows[0] ? new Invoice(rows[0]) : null;
  }

  async getItemsByInvoiceId(invoiceId) {
    const [rows] = await pool.query(
      `SELECT * FROM invoice_items WHERE invoice_id = ? ORDER BY id ASC`,
      [invoiceId],
    );
    return rows || [];
  }

  async updateStorageKey(invoiceId, storageKey) {
    await pool.query(
      `UPDATE invoices SET pdf_storage_key = ?, updated_at = NOW() WHERE id = ?`,
      [storageKey, invoiceId],
    );
    return this.getById(invoiceId);
  }

  async listByUser(userId, { status, limit = 50, offset = 0 } = {}) {
    const params = [userId];
    let sql = `SELECT * FROM invoices WHERE user_id = ?`;
    if (status) {
      sql += ` AND status = ?`;
      params.push(status);
    }
    sql += ` ORDER BY issued_at DESC, created_at DESC LIMIT ? OFFSET ?`;
    params.push(Number(limit), Number(offset));
    const [rows] = await pool.query(sql, params);
    return rows.map((r) => new Invoice(r));
  }

  async listAll({ status, limit = 200, offset = 0 } = {}) {
    const params = [];
    let sql = `SELECT * FROM invoices`;
    if (status) {
      sql += ` WHERE status = ?`;
      params.push(status);
    }
    sql += ` ORDER BY issued_at DESC, created_at DESC LIMIT ? OFFSET ?`;
    params.push(Number(limit), Number(offset));
    const [rows] = await pool.query(sql, params);
    return rows.map((r) => new Invoice(r));
  }

  async findByAbonement(abonementId) {
    const [rows] = await pool.query(
      `SELECT * FROM invoices WHERE source_type='subscription' AND source_id = ? ORDER BY issued_at DESC, id DESC`,
      [abonementId],
    );
    return rows.map((r) => new Invoice(r));
  }

  async getPaidRevenueSummary() {
    const [rows] = await pool.query(
      `SELECT COALESCE(SUM(total_gross), 0) AS total_paid_all_time,
              COALESCE(MAX(currency), 'EUR') AS currency,
              COUNT(*) AS paid_invoice_count
       FROM invoices
       WHERE status = 'paid'`
    );

    return {
      total_paid_all_time: Number(rows?.[0]?.total_paid_all_time || 0),
      currency: rows?.[0]?.currency || 'EUR',
      paid_invoice_count: Number(rows?.[0]?.paid_invoice_count || 0),
    };
  }

  async markIssuedPastDueAsOverdue() {
    const [result] = await pool.query(
      `UPDATE invoices
       SET status = 'overdue', updated_at = NOW()
       WHERE status = 'issued'
         AND due_at IS NOT NULL
         AND due_at < CURDATE()`
    );
    return result?.affectedRows || 0;
  }

  async updateStatus(invoiceId, newStatus) {
    const allowed = ['draft', 'issued', 'paid', 'overdue', 'canceled'];
    if (!allowed.includes(newStatus)) {
      throw new Error(`Invalid status '${newStatus}'. Allowed: ${allowed.join(', ')}`);
    }
    await pool.query(
      `UPDATE invoices SET status = ?, updated_at = NOW() WHERE id = ?`,
      [newStatus, invoiceId],
    );
    return this.getById(invoiceId);
  }

  async getPaymentsByInvoiceId(invoiceId) {
    const [rows] = await pool.query(
      `SELECT * FROM invoice_payments WHERE invoice_id = ? ORDER BY created_at DESC`,
      [invoiceId],
    );
    return rows || [];
  }

  async createManualInvoice({
    source_type = 'manual',
    source_id = null,
    user_id = null,
    buyer_name = null,
    buyer_email = null,
    buyer_street = null,
    buyer_postal_code = null,
    buyer_city = null,
    buyer_country = null,
    currency = 'EUR',
    total_net = 0,
    total_tax = 0,
    total_gross = 0,
    vat_rate = null,
    status = 'issued',
    issued_at = null,
    due_at = null,
    context = null,
  }) {
    const invoice_number = await genInvoiceNumber();
    const [res] = await pool.query(
      `INSERT INTO invoices
       (invoice_number, user_id, source_type, source_id, buyer_name, buyer_email, buyer_street, buyer_postal_code, buyer_city, buyer_country,
        currency, total_net, total_tax, total_gross, vat_rate, status, issued_at, due_at, pdf_storage_key, context, created_at, updated_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, NOW(), NOW())`,
      [
        invoice_number,
        user_id || null,
        source_type,
        source_id || 0,
        buyer_name || null,
        buyer_email || null,
        buyer_street || null,
        buyer_postal_code || null,
        buyer_city || null,
        buyer_country || null,
        currency,
        +Number(total_net).toFixed(2),
        +Number(total_tax).toFixed(2),
        +Number(total_gross).toFixed(2),
        vat_rate != null ? Number(vat_rate) : null,
        status,
        issued_at || null,
        due_at || null,
        context ? JSON.stringify(context) : null,
      ],
    );
    return this.getById(res.insertId);
  }
}

module.exports = InvoiceRepository;
