const pool = require('../../database/database');
const AbonemmentRepository = require('../../repositories/abonemments/AbonemmentRepository');
const InvoiceService = require('../invoice/InvoiceService'); // NEW
const AboContractService = require('./AboContractService');
const UnitOfWork = require('../../database/UnitOfWork');
const ReferralService = require('../referral/ReferralService');
const ReferralTokenRepository = require('../../repositories/referral/ReferralTokenRepository');
const MailService = require('../email/MailService');

const CAPSULES_PER_PACK = 10;
const MIN_ABO_PACKS = 6;
const MAX_ABO_PACKS = 10000;

function normalizeUid(value) {
  return String(value || '').trim().toUpperCase().replace(/[^A-Z0-9]/g, '');
}

function getPackCountError(totalPacks) {
  if (totalPacks < MIN_ABO_PACKS) {
    return `Order must contain at least ${MIN_ABO_PACKS} packs (${MIN_ABO_PACKS * CAPSULES_PER_PACK} capsules).`;
  }

  if (totalPacks > MAX_ABO_PACKS) {
    return `Order cannot contain more than ${MAX_ABO_PACKS} packs (${MAX_ABO_PACKS * CAPSULES_PER_PACK} capsules).`;
  }

  return null;
}

class AbonemmentService {
  constructor() {
    this.repo = new AbonemmentRepository();
    this.invoiceService = new InvoiceService(); // NEW
    this.aboContractService = new AboContractService();
  }

  isAdmin(user) {
    return user && ['admin', 'super_admin'].includes(user.role);
  }

  addInterval(date, interval, count) {
    const d = new Date(date);
    if (interval === 'day') d.setDate(d.getDate() + count);
    if (interval === 'week') d.setDate(d.getDate() + 7 * count);
    if (interval === 'month') d.setMonth(d.getMonth() + count);
    if (interval === 'year') d.setFullYear(d.getFullYear() + count);
    return d;
  }

  async getCoffeeProduct(id) {
    const [rows] = await pool.query(
      `SELECT id, title, price, currency, billing_interval, interval_count, state AS is_active, pack_group FROM coffee_table WHERE id = ?`,
      [id],
    );
    return rows[0] || null;
  }

  // Helper: normalize email
  normalizeEmail(email) {
    return typeof email === 'string' ? email.trim().toLowerCase() : null;
  }

  // NEW: single bundle subscribe using items array (pack-based order content)
  async subscribeOrder({
    items,
    billingInterval,
    intervalCount,
    isAutoRenew,
    isForSelf,
    recipientName,
    recipientEmail,
    firstName,
    lastName,
    email,
    street,
    postalCode,
    city,
    country,
    frequency,
    phone,
    recipientContractName,
    recipientAddress,
    paymentMethod,
    invoiceByEmail,
    invoiceSameAsShipping,
    invoiceFullName,
    invoiceStreet,
    invoicePostalCode,
    invoiceCity,
    invoicePhone,
    invoiceEmail,
    uidNumber,
    atuNumber,
    taxMode,
    contractNumber,
    signingCity,
    signatureDataUrl,
    actorUser,
    referredBy, // NEW: referred_by field
  }) {
    console.log('[SUBSCRIBE ORDER] Start processing subscription order');
    console.log('[SUBSCRIBE ORDER] Actor user:', actorUser ? { id: actorUser.id, role: actorUser.role, email: actorUser.email } : actorUser);
    console.log('[SUBSCRIBE ORDER] Payload:', {
      firstName,
      lastName,
      email,
      street,
      postalCode,
      city,
      country,
      frequency,
    });

    const normalizedEmail = this.normalizeEmail(email);
    const normalizedRecipientEmail = this.normalizeEmail(recipientEmail);
    const forSelf = isForSelf !== false && !normalizedRecipientEmail;

    if (typeof signingCity !== 'string' || signingCity.trim() === '') {
      throw new Error('signingCity is required');
    }

    if (typeof signatureDataUrl !== 'string' || signatureDataUrl.trim() === '') {
      throw new Error('signatureDataUrl is required');
    }

    if (!forSelf && !normalizedRecipientEmail) {
      throw new Error('recipient_email is required when subscription is for another person');
    }

    if (!Array.isArray(items) || items.length === 0) throw new Error('items must be a non-empty array');

    let totalPacks = 0;
    let totalPrice = 0;
    const breakdown = [];

    for (const item of items) {
      const coffeeId = item?.coffeeId;
      const packs = Number(item?.quantity ?? 0);
      if (!coffeeId) throw new Error('coffeeId is required for each item');
      if (!Number.isInteger(packs) || packs <= 0) throw new Error('quantity must be a positive integer per item');

      const product = await this.getCoffeeProduct(coffeeId);
      if (!product || !product.is_active) throw new Error(`Product ${coffeeId} not available`);

      const capsulePrice = Number(product.price);
      const packPrice = capsulePrice * 10; // 10 capsules per pack

      totalPacks += packs;
      totalPrice += packs * packPrice;

      breakdown.push({
        coffee_table_id: coffeeId,
        coffee_title: product.title || null,
        packs,
        price_per_pack: packPrice,
        currency: product.currency,
      });
    }

    const packCountError = getPackCountError(totalPacks);
    if (packCountError) throw new Error(packCountError);

    const now = new Date();
    const nextBilling = this.addInterval(now, billingInterval || 'month', intervalCount || 1);
    const normalizedInvoiceUid = normalizeUid(uidNumber || atuNumber || '');

    const effectiveRecipientName = recipientName || `${firstName || ''} ${lastName || ''}`.trim() || null;
    const effectiveEmail = forSelf ? normalizedEmail : normalizedRecipientEmail;

    const snapshot = {
      status: 'active',
      started_at: now,
      next_billing_at: nextBilling,
      billing_interval: billingInterval || 'month',
      interval_count: intervalCount || 1,
      price: Number(totalPrice.toFixed(2)),
      currency: breakdown[0]?.currency || 'EUR',
      is_auto_renew: isAutoRenew !== false,
      actor_user_id: actorUser?.id,
      details: {
        origin: 'subscribe_order',
        total_packs: totalPacks,
        is_for_self: forSelf,
        recipient_name: forSelf ? null : effectiveRecipientName,
        tax_mode_hint: taxMode || null,
        invoice_uid_number: normalizedInvoiceUid || null,
      },
      pack_breakdown: breakdown,
      first_name: forSelf ? firstName : (effectiveRecipientName || firstName),
      last_name: forSelf ? lastName : null,
      email: effectiveEmail,
      street,
      postal_code: postalCode,
      city,
      country,
      frequency,
      referred_by: referredBy || (forSelf ? (actorUser?.id ?? null) : null),
      user_id: forSelf ? (actorUser?.id ?? null) : null,
      purchaser_user_id: actorUser?.id ?? null, // NEW: also store purchaser
      // New fields
      phone: phone || null,
      recipient_name: recipientContractName || null,
      recipient_address: recipientAddress || null,
      payment_method: paymentMethod || null,
      invoice_by_email: !!invoiceByEmail,
      invoice_same_as_shipping: invoiceSameAsShipping !== false,
      invoice_full_name: invoiceFullName || null,
      invoice_street: invoiceStreet || null,
      invoice_postal_code: invoicePostalCode || null,
      invoice_city: invoiceCity || null,
      invoice_phone: invoicePhone || null,
      invoice_email: invoiceEmail || null,
    };

    console.log('[SUBSCRIBE ORDER] Snapshot user linking:', {
      actor_user_id: snapshot.actor_user_id,
      user_id: snapshot.user_id,
      purchaser_user_id: snapshot.purchaser_user_id,
      referred_by: snapshot.referred_by,
    });

    const abonement = await this.repo.createAbonement(referredBy, snapshot); // Pass referredBy to repository
    console.log('[SUBSCRIBE ORDER] Created abonement:', {
      id: abonement?.id,
      user_id: abonement?.user_id,
      purchaser_user_id: abonement?.purchaser_user_id,
      status: abonement?.status,
      pack_group: abonement?.pack_group,
    });

    // NEW: issue ABO contract PDF (best-effort)
    if (signatureDataUrl) {
      try {
        if (!abonement.contract_storage_key) {
          const lang = actorUser?.lang || actorUser?.language || 'en';
          const issued = await this.aboContractService.generateUploadAndEmail({
            abonement,
            actorUser,
            contractNumber,
            signingCity,
            signatureDataUrl,
            isForSelf: forSelf,
            lang,
          });

          await this.repo.updateContractInfo(abonement.id, {
            contractNumber: issued.contractNumber,
            storageKey: issued.storageKey,
          });

          await this.repo.appendHistory(
            abonement.id,
            'abo_contract_issued',
            actorUser?.id || null,
            { contractNumber: issued.contractNumber, storageKey: issued.storageKey, pack_group: abonement.pack_group },
            new Date(),
          );
        }
      } catch (e) {
        console.error('[SUBSCRIBE ORDER] ABO contract generation failed:', e);
        try {
          await this.repo.appendHistory(
            abonement.id,
            'abo_contract_failed',
            actorUser?.id || null,
            { message: e?.message || String(e), pack_group: abonement.pack_group },
            new Date(),
          );
        } catch (_) {}
      }
    } else {
      console.warn('[SUBSCRIBE ORDER] No signatureDataUrl provided; skipping ABO contract PDF generation.');
    }

    // NEW: issue invoice for first period and append history
    try {
      const invoice = await this.invoiceService.issueForAbonement(
        abonement,
        snapshot.started_at,
        snapshot.next_billing_at,
        {
          actorUserId: actorUser?.id || null,
          lang: actorUser?.lang || actorUser?.language || 'en',
          uidOverride: normalizedInvoiceUid || null,
          taxModeHint: taxMode || null,
        }
      );
      console.log('[SUBSCRIBE ORDER] Issued invoice:', {
        id: invoice?.id,
        user_id: invoice?.user_id,
        total_gross: invoice?.total_gross,
        vat_rate: invoice?.vat_rate,
      });
      await this.repo.appendHistory(
        abonement.id,
        'invoice_issued',
        actorUser?.id || null,
        { pack_group: abonement.pack_group, invoiceId: invoice.id },
        new Date()
      );
    } catch (e) {
      console.error('[SUBSCRIBE ORDER] Invoice issue failed:', e);
      // intentionally not throwing to avoid blocking subscription; adjust if you want transactional consistency
    }

    if (!forSelf && effectiveEmail) {
      const existingUser = await this.findUserByEmail(effectiveEmail);
      console.log('[SUBSCRIBE ORDER] Gift flow:', { forSelf, effectiveEmail, existingUser: existingUser?.id || null });
      if (!existingUser) {
        await this.repo.upsertNoUserAboMail(effectiveEmail, abonement.id, actorUser?.id || null);
        const referralLink = await this.getOrCreateReferralLink(actorUser?.id || null);
        console.log('[SUBSCRIBE ORDER] Referral link generated:', referralLink);
        if (referralLink) {
          try {
            await MailService.sendSubscriptionInvitationEmail({
              email: effectiveEmail,
              inviterName: actorUser?.email || 'ProfitPlanet user',
              referralLink,
              lang: actorUser?.lang || actorUser?.language || 'en'
            });
          } catch (mailError) {
            console.error('[SUBSCRIBE ORDER] Invitation email failed:', mailError);
          }
        }
      }
    }

    return abonement;
  }

  async findUserByEmail(email) {
    const normalized = this.normalizeEmail(email);
    if (!normalized) return null;
    const [rows] = await pool.query(
      `SELECT id, email FROM users WHERE email = ? LIMIT 1`,
      [normalized]
    );
    return rows && rows[0] ? rows[0] : null;
  }

  async getOrCreateReferralLink(userId) {
    if (!userId) return null;
    const unitOfWork = new UnitOfWork();
    await unitOfWork.start();
    try {
      // Always create a fresh single-use token for each gift invitation
      const created = await ReferralService.createReferralToken({
        userId,
        expiresInDays: 7,
        maxUses: 1,
        unitOfWork,
      });
      const tokenValue = created?.token;
      console.log('[REFERRAL LINK] Created new gift token:', tokenValue);

      await unitOfWork.commit();
      if (!tokenValue) return null;

      const base = (process.env.FRONTEND_URL || 'https://profit-planet.partners').replace(/\/$/, '');
      return `${base}/register?ref=${tokenValue}&guest=true`;
    } catch (error) {
      await unitOfWork.rollback(error);
      console.error('[ABONEMENT] getOrCreateReferralLink failed:', error);
      return null;
    }
  }

  async subscribe({
    userId,
    coffeeId,
    billingInterval,
    intervalCount,
    isAutoRenew,
    targetUserId,
    recipientName,
    recipientEmail,
    recipientNotes,
    contractNumber,
    signingCity,
    signatureDataUrl,
    actorUser,
    referredBy, // NEW: referred_by field
  }) {
    console.log('[SUBSCRIBE] Start processing single subscription'); // NEW
    console.log('[SUBSCRIBE] Actor user:', actorUser ? { id: actorUser.id, role: actorUser.role, email: actorUser.email } : actorUser);
    console.log('[SUBSCRIBE] Incoming userId:', userId, 'targetUserId:', targetUserId); // NEW

    const normalizedRecipientEmail = this.normalizeEmail(recipientEmail);
    console.log('[SUBSCRIBE] Normalized recipient email:', normalizedRecipientEmail); // NEW

    if (typeof signingCity !== 'string' || signingCity.trim() === '') {
      throw new Error('signingCity is required');
    }

    if (typeof signatureDataUrl !== 'string' || signatureDataUrl.trim() === '') {
      throw new Error('signatureDataUrl is required');
    }

    if (coffeeId === undefined || coffeeId === null) throw new Error('coffeeId is required');

    const hasRecipientFields = recipientName || normalizedRecipientEmail || recipientNotes;
    if (targetUserId && hasRecipientFields) throw new Error('Provide either target_user_id or recipient fields, not both');
    if (hasRecipientFields && !normalizedRecipientEmail) throw new Error('recipient_email is required when subscribing for another person');

    const safeUserId = userId ?? null;
    const isForMe = !targetUserId && !hasRecipientFields;
    const ownerUserId = targetUserId ?? (hasRecipientFields ? null : safeUserId);
    const purchaserUserId = isForMe ? null : actorUser?.id || null;
    console.log('[SUBSCRIBE] Resolved ownership:', { isForMe, ownerUserId, purchaserUserId }); // NEW

    const recipientMeta = targetUserId
      ? { target_user_id: targetUserId }
      : hasRecipientFields
        ? { recipient_name: recipientName, recipient_email: normalizedRecipientEmail, recipient_notes: recipientNotes }
        : null;

    const product = await this.getCoffeeProduct(coffeeId);
    if (!product || !product.is_active) throw new Error('Product not available');

    const canonicalPackGroup = product.pack_group || `product:${coffeeId}`;
    const now = new Date();
    const nextBilling = this.addInterval(
      now,
      billingInterval || product.billing_interval,
      intervalCount || product.interval_count || 1,
    );

    const details = { origin: 'subscribe', pack_group: canonicalPackGroup };
    if (recipientMeta) details.recipient = recipientMeta;

    const snapshot = {
      status: 'active',
      pack_group: canonicalPackGroup,
      started_at: now,
      next_billing_at: nextBilling,
      billing_interval: billingInterval || product.billing_interval,
      interval_count: intervalCount || product.interval_count || 1,
      price: product.price,
      currency: product.currency,
      is_auto_renew: isAutoRenew !== false,
      actor_user_id: actorUser?.id,
      notes: recipientMeta && recipientMeta.recipient_name ? recipientMeta.recipient_name : undefined,
      recipient_email: normalizedRecipientEmail || null, // CHANGED
      details,
      recipient: recipientMeta || undefined,
      pack_breakdown: [{
        coffee_table_id: product.id,
        coffee_title: product.title || null,
        packs: 1,
        price_per_pack: Number(product.price) * 10, // 10 capsules per pack
        currency: product.currency,
      }],
      purchaser_user_id, // NEW
      user_id: ownerUserId ?? null, // NEW: persist owner
      referred_by: referredBy || null, // Pass referred_by to snapshot
    };

    console.log('[SUBSCRIBE] Snapshot user linking:', {
      actor_user_id: snapshot.actor_user_id,
      user_id: snapshot.user_id,
      purchaser_user_id: snapshot.purchaser_user_id,
      referred_by: snapshot.referred_by,
    });

    const existing = await this.repo.findActiveOrPausedByUserAndProduct(ownerUserId ?? null, canonicalPackGroup);
    console.log('[SUBSCRIBE] Existing abonement candidate:', existing ? { id: existing.id, user_id: existing.user_id } : null); // NEW
    const abonement = existing
      ? await this.repo.updateExistingAbonementForSubscribe(existing.id, snapshot)
      : await this.repo.createAbonement(ownerUserId ?? null, snapshot, referredBy); // Pass referredBy to repository

    console.log('[SUBSCRIBE] Upserted abonement:', {
      id: abonement?.id,
      user_id: abonement?.user_id,
      purchaser_user_id: abonement?.purchaser_user_id,
      status: abonement?.status,
      pack_group: abonement?.pack_group,
    });

    // NEW: only generate contract when this is a new subscription (not merging into existing)
    if (!existing) {
      if (signatureDataUrl) {
        try {
          if (!abonement.contract_storage_key) {
            const lang = actorUser?.lang || actorUser?.language || 'en';
            const issued = await this.aboContractService.generateUploadAndEmail({
              abonement,
              actorUser,
              contractNumber,
              signingCity,
              signatureDataUrl,
              isForSelf: isForMe,
              lang,
            });

            await this.repo.updateContractInfo(abonement.id, {
              contractNumber: issued.contractNumber,
              storageKey: issued.storageKey,
            });

            await this.repo.appendHistory(
              abonement.id,
              'abo_contract_issued',
              actorUser?.id || null,
              { contractNumber: issued.contractNumber, storageKey: issued.storageKey, pack_group: abonement.pack_group },
              new Date(),
            );
          }
        } catch (e) {
          console.error('[SUBSCRIBE] ABO contract generation failed:', e);
          try {
            await this.repo.appendHistory(
              abonement.id,
              'abo_contract_failed',
              actorUser?.id || null,
              { message: e?.message || String(e), pack_group: abonement.pack_group },
              new Date(),
            );
          } catch (_) {}
        }
      } else {
        console.warn('[SUBSCRIBE] No signatureDataUrl provided; skipping ABO contract PDF generation.');
      }
    }

    // NEW: issue invoice for first period and append history
    try {
      const invoice = await this.invoiceService.issueForAbonement(
        abonement,
        snapshot.started_at,
        snapshot.next_billing_at,
        {
          actorUserId: actorUser?.id || null,
          lang: actorUser?.lang || actorUser?.language || 'en'
        }
      );
      console.log('[SUBSCRIBE] Issued invoice:', {
        id: invoice?.id,
        user_id: invoice?.user_id,
        total_gross: invoice?.total_gross,
        vat_rate: invoice?.vat_rate,
      });
      await this.repo.appendHistory(
        abonement.id,
        'invoice_issued',
        actorUser?.id || null,
        { pack_group: abonement.pack_group, invoiceId: invoice.id },
        new Date()
      );
    } catch (e) {
      console.error('[SUBSCRIBE] Invoice issue failed:', e);
    }

    console.log('[SUBSCRIBE] Single subscription completed successfully'); // NEW
    return abonement;
  }

  // NEW: authorization helper
  canManageAbonement(abon, actorUser) {
    if (this.isAdmin(actorUser)) return true;
    const actorId = actorUser?.id;
    if (!actorId) return false;
    if (abon.user_id && abon.user_id === actorId) return true;
    if (!abon.user_id && abon.purchaser_user_id && abon.purchaser_user_id === actorId) return true;
    return false;
  }

  async pause({ abonementId, actorUser }) {
    const abon = await this.repo.getAbonementById(abonementId);
    if (!abon) throw new Error('Not found');
    if (!this.canManageAbonement(abon, actorUser)) throw new Error('Forbidden'); // NEW
    if (!abon.isActive) throw new Error('Only active abonements can be paused');
    return this.repo.transitionStatus(abonementId, 'paused', {
      event_type: 'paused',
      actor_user_id: actorUser?.id,
      details: { pack_group: abon.pack_group },
    });
  }

  async resume({ abonementId, actorUser }) {
    const abon = await this.repo.getAbonementById(abonementId);
    if (!abon) throw new Error('Not found');
    if (!this.canManageAbonement(abon, actorUser)) throw new Error('Forbidden'); // NEW
    if (!abon.isPaused) throw new Error('Only paused abonements can be resumed');
    return this.repo.transitionStatus(abonementId, 'active', {
      event_type: 'resumed',
      actor_user_id: actorUser?.id,
      details: { pack_group: abon.pack_group },
    });
  }

  async cancel({ abonementId, actorUser }) {
    const abon = await this.repo.getAbonementById(abonementId);
    if (!abon) throw new Error('Not found');
    if (!this.canManageAbonement(abon, actorUser)) throw new Error('Forbidden'); // NEW
    if (abon.isCanceled) return abon;
    return this.repo.transitionStatus(abonementId, 'canceled', {
      event_type: 'canceled',
      actor_user_id: actorUser?.id,
      ended_at: new Date(),
      details: { pack_group: abon.pack_group },
    });
  }

  async updateContent({ abonementId, actorUser, items }) {
    const abon = await this.repo.getAbonementById(abonementId);
    if (!abon) throw new Error('Not found');
    if (!this.canManageAbonement(abon, actorUser)) throw new Error('Forbidden');
    if (!['active', 'paused'].includes(abon.status)) {
      throw new Error('Only active or paused abonements can be updated');
    }

    if (!Array.isArray(items) || items.length === 0) {
      throw new Error('items must be a non-empty array');
    }

    let totalPacks = 0;
    let totalPrice = 0;
    const breakdown = [];

    for (const item of items) {
      const coffeeId = item?.coffeeId;
      const packs = Number(item?.quantity ?? 0);
      if (!coffeeId) throw new Error('coffeeId is required for each item');
      if (!Number.isInteger(packs) || packs <= 0) {
        throw new Error('quantity must be a positive integer per item');
      }

      const product = await this.getCoffeeProduct(coffeeId);
      if (!product || !product.is_active) throw new Error(`Product ${coffeeId} not available`);

      const capsulePrice = Number(product.price);
      const packPrice = capsulePrice * 10; // 10 capsules per pack

      totalPacks += packs;
      totalPrice += packs * packPrice;
      breakdown.push({
        coffee_table_id: coffeeId,
        coffee_title: product.title || null,
        packs,
        price_per_pack: packPrice,
        currency: product.currency,
      });
    }

    const packCountError = getPackCountError(totalPacks);
    if (packCountError) throw new Error(packCountError);

    const previousPacks = Array.isArray(abon.pack_breakdown)
      ? abon.pack_breakdown.reduce((sum, item) => sum + Number(item?.packs || item?.quantity || 0), 0)
      : null;

    return this.repo.transitionContent(abonementId, {
      pack_breakdown: breakdown,
      price: Number(totalPrice.toFixed(2)),
      currency: breakdown[0]?.currency || abon.currency || 'EUR',
      actor_user_id: actorUser?.id || null,
      event_type: 'content_updated',
      details: {
        pack_group: abon.pack_group,
        previous_total_packs: previousPacks,
        total_packs: totalPacks,
        effective_from: 'next_billing_cycle',
      },
    });
  }

  async renew({ abonementId, actorUser, invoiceId }) {
    const abon = await this.repo.getAbonementById(abonementId);
    if (!abon) throw new Error('Not found');
    if (!abon.isActive) throw new Error('Only active abonements can be renewed');
    const next = this.addInterval(new Date(abon.next_billing_at || new Date()), abon.billing_interval, abon.interval_count);
    const renewed = await this.repo.transitionBilling(abonementId, next, {
      event_type: 'renewed',
      actor_user_id: actorUser?.id || null,
      details: { pack_group: abon.pack_group, ...(invoiceId ? { invoiceId } : {}) },
    });

    // NEW: issue invoice for next cycle and attach to history
    try {
      const invoice = await this.invoiceService.issueForAbonement(
        renewed,
        new Date(abon.next_billing_at || new Date()),
        next,
        {
          actorUserId: actorUser?.id || null,
          lang: actorUser?.lang || actorUser?.language || 'en'
        }
      );
      await this.repo.appendHistory(
        abonementId,
        'invoice_issued',
        actorUser?.id || null,
        { pack_group: renewed.pack_group, invoiceId: invoice.id },
        new Date()
      );
    } catch (e) {
      console.error('[RENEW] Invoice issue failed:', e);
    }

    return renewed;
  }

  async getMyAbonements({ userId }) {
    return this.repo.listByUser(userId);
  }

  async getMyAbonementStatus({ userId }) {
    const list = await this.repo.listByUser(userId);
    const current = list.find((a) => ['active', 'paused'].includes(a.status)) || list[0] || null;
    return {
      hasAbo: Boolean(current),
      abonement: current,
    };
  }

  async getInvoicesForAbonement({ abonementId, actorUser }) {
    const abon = await this.repo.getAbonementById(abonementId);
    if (!abon) {
      throw new Error('Not found');
    }
    if (!this.canManageAbonement(abon, actorUser)) {
      throw new Error('Forbidden');
    }
    return this.invoiceService.listByAbonement(abonementId);
  }

  async getHistory({ abonementId }) {
    return this.repo.listHistory(abonementId);
  }

  async linkGiftFlagsToUser(email, userId) {
    const normalizedEmail = this.normalizeEmail(email); // NEW
    const pending = await this.repo.findPendingNoUserAboMailsByEmail(normalizedEmail);
    for (const row of pending) {
      await this.repo.linkAbonementToUser(row.abonement_id, userId);
      await this.repo.appendHistory(
        row.abonement_id,
        'gift_linked',
        userId,
        { email, pack_group: (await this.repo.getAbonementById(row.abonement_id))?.pack_group || null }
      );
      await this.repo.markNoUserAboMailLinked(row.id); // CHANGED
    }
    return pending.length;
  }

  async adminList({ status }) {
    const [rows] = await pool.query(
      `SELECT * FROM coffee_abonements ${status ? 'WHERE status = ?' : ''} ORDER BY created_at DESC LIMIT 200`,
      status ? [status] : [],
    );
    return rows;
  }

  async adminRenew({ abonementId, actorUser }) {
    if (!this.isAdmin(actorUser)) throw new Error('Forbidden');
    return this.renew({ abonementId, actorUser });
  }

  async getReferredSubscriptions({ userId, email }) {
    if (!userId || !email) throw new Error('User ID and email are required');
    const rows = await this.repo.findByReferredByAndEmail(userId, email);

    // Collect distinct coffee_table_ids from pack_breakdown
    const idsSet = new Set();
    for (const r of rows) {
      const breakdown = Array.isArray(r.pack_breakdown) ? r.pack_breakdown : [];
      for (const item of breakdown) {
        const id = item?.coffee_table_id;
        if (id !== undefined && id !== null) idsSet.add(Number(id));
      }
    }
    const ids = Array.from(idsSet);
    let nameMap = {};
    if (ids.length) {
      const [nameRows] = await pool.query(
        `SELECT id, title FROM coffee_table WHERE id IN (${ids.map(() => '?').join(',')})`,
        ids
      );
      nameMap = (nameRows || []).reduce((acc, row) => {
        acc[Number(row.id)] = row.title; // CHANGED: use title
        return acc;
      }, {});
    }

    // Attach coffee_name to each pack_breakdown item
    const enriched = rows.map(r => {
      const breakdown = Array.isArray(r.pack_breakdown) ? r.pack_breakdown : [];
      const withNames = breakdown.map(item => ({
        ...item,
        coffee_name: nameMap[Number(item.coffee_table_id)] || null,
      }));
      // Return a plain object with enriched breakdown
      return {
        ...r,
        pack_breakdown: withNames,
      };
    });

    return enriched;
  }
}

module.exports = AbonemmentService;