const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer');
const { PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
const { s3: sharedExoscaleClient } = require('../../utils/exoscaleUploader');
const DocumentTemplateService = require('../template/DocumentTemplateService');
const MailService = require('../email/MailService');
const pool = require('../../database/database');
const { logger } = require('../../middleware/logger');
const { normalizeLanguageCode } = require('../../utils/languageUtils');

class AboContractService {
  constructor() {
    this.templateDir = path.join(__dirname, '..', '..', 'templates', 'abo');
  }

  /**
   * Load the latest active abo contract template from the contract management system (DB + S3).
   * Falls back to the local file if no active template is found.
   */
  async _loadTemplate(userType = 'both', lang = null) {
    try {
      const latest = await DocumentTemplateService.getLatestActiveForUserType(userType, 'contract', 'abo', null, lang);
      if (latest?.storageKey) {
        const command = new GetObjectCommand({
          Bucket: process.env.EXOSCALE_BUCKET,
          Key: latest.storageKey,
        });
        const fileObj = await sharedExoscaleClient.send(command);
        if (fileObj.Body) {
          const chunks = [];
          for await (const chunk of fileObj.Body) {
            chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
          }
          const html = Buffer.concat(chunks).toString('utf-8');
          if (html.trim()) {
            logger.info('AboContractService:template_loaded_from_s3', { id: latest.id, storageKey: latest.storageKey });
            return html;
          }
        }
      }
    } catch (e) {
      logger.warn('AboContractService:s3_template_load_failed', { message: e?.message });
    }

    const normalizedLang = normalizeLanguageCode(lang);
    const candidates = [];
    if (normalizedLang === 'sl') candidates.push('abo-contract-SL.html');
    if (normalizedLang === 'de') candidates.push('abo-contract-DE.html');
    if (normalizedLang !== 'de') candidates.push('abo-contract-DE.html');

    for (const fileName of candidates) {
      const fallbackPath = path.join(this.templateDir, fileName);
      if (!fs.existsSync(fallbackPath)) continue;
      logger.warn('AboContractService:using_local_fallback_template', { fileName, lang: normalizedLang || null });
      return fs.readFileSync(fallbackPath, 'utf8');
    }

    throw new Error('ABO contract template missing');
  }

  _escapeHtml(value) {
    return String(value ?? '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  _formatDateTime(d = new Date()) {
    const pad = (n) => String(n).padStart(2, '0');
    return `${pad(d.getDate())}.${pad(d.getMonth() + 1)}.${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
  }

  _sanitizeKeyPart(input, fallback) {
    const raw = String(input ?? '').trim();
    const safe = raw.replace(/[^a-zA-Z0-9._-]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 80);
    return safe || fallback;
  }

  _renderTemplate(template, variables, { rawKeys = new Set() } = {}) {
    if (!template) return '';
    return template.replace(/{{\s*([\w]+)\s*}}/g, (_, key) => {
      const value = variables[key];
      if (value === undefined || value === null) return '';
      if (rawKeys.has(key)) return String(value);
      return this._escapeHtml(String(value));
    });
  }

  _buildSelectedProductsHtml(abonement) {
    const breakdown = Array.isArray(abonement?.pack_breakdown) ? abonement.pack_breakdown : [];
    if (!breakdown.length) return '<p>-</p>';

    const rows = breakdown.map((item) => {
      const title = this._escapeHtml(item?.coffee_title || item?.title || 'Coffee');
      const packs = Number(item?.packs ?? item?.quantity ?? 0);
      return `<li>${title} — ${Number.isFinite(packs) ? packs : 0} pack(s)</li>`;
    }).join('');

    return `<ul>${rows}</ul>`;
  }

  _buildSignatureImgHtml(signatureDataUrl) {
    const s = typeof signatureDataUrl === 'string' ? signatureDataUrl.trim() : '';
    if (!s) return '';
    // Basic safety: only accept data:image/... urls
    if (!/^data:image\//i.test(s)) return '';
    const escaped = this._escapeHtml(s);
    return `<img alt="Signature" src="${escaped}" style="max-width:280px;max-height:120px;" />`;
  }

  async _renderPdfFromHtml(html) {
    const browser = await puppeteer.launch({
      headless: 'new',
      args: ['--no-sandbox', '--disable-setuid-sandbox'],
    });
    try {
      const page = await browser.newPage();
      await page.setContent(html, { waitUntil: 'networkidle0' });
      const pdfBuffer = await page.pdf({
        format: 'A4',
        printBackground: true,
        margin: { top: '16mm', right: '14mm', bottom: '16mm', left: '14mm' },
      });
      return Buffer.isBuffer(pdfBuffer) ? pdfBuffer : Buffer.from(pdfBuffer);
    } finally {
      await browser.close();
    }
  }

  async generateUploadAndEmail({
    abonement,
    actorUser,
    contractNumber,
    signingCity,
    signatureDataUrl,
    isForSelf,
    lang = 'en',
  }) {
    if (!abonement?.id) throw new Error('abonement is required');

    // Load template from contract management system (DB + S3), fallback to local file
    const userType = await this._resolveUserType(actorUser);
    let template;
    try {
      template = await this._loadTemplate(userType, lang);
    } catch (e) {
      logger.error('AboContractService:template_missing', { message: e?.message });
      throw new Error('ABO contract template missing');
    }

    // --- Sequential contract number ---
    let displayContractNumber = String(contractNumber || '').trim();
    if (!displayContractNumber) {
      displayContractNumber = await this._generateSequentialContractNumber();
    }
    const contractKeyPart = this._sanitizeKeyPart(displayContractNumber, `abo-${abonement.id}`);

    let profitplanetSignature = '';
    try {
      const sig = await DocumentTemplateService.getProfitPlanetSignatureTag({ maxW: 300, maxH: 300 });
      profitplanetSignature = sig?.tag || '';
      logger.info('AboContractService:stamp_result', { reason: sig?.reason, hasTag: !!profitplanetSignature, tagLen: profitplanetSignature.length });
    } catch (e) {
      logger.warn('AboContractService:getProfitPlanetSignatureTag_failed', { message: e?.message });
    }

    // --- Determine user type from actorUser or DB ---
    // (userType already resolved above for template loading)
    const isCompany = userType === 'company';

    // --- Company data (FN / ATU) ---
    let fnNumber = '';
    let atuNumber = '';
    if (isCompany && actorUser?.id) {
      const companyData = await this._loadCompanyData(actorUser.id);
      fnNumber = companyData.registrationNumber || '';
      atuNumber = companyData.atuNumber || '';
    }

    // --- Shipping & Invoice data ---
    const fullName = `${abonement.first_name || ''} ${abonement.last_name || ''}`.trim();
    const invoiceSame = abonement.invoice_same_as_shipping !== false;

    // DEBUG: log abonement data to diagnose empty fields in PDF
    logger.info('AboContractService:abonement_data', {
      id: abonement.id,
      first_name: abonement.first_name,
      last_name: abonement.last_name,
      email: abonement.email,
      street: abonement.street,
      postal_code: abonement.postal_code,
      city: abonement.city,
      phone: abonement.phone,
      payment_method: abonement.payment_method,
      invoice_by_email: abonement.invoice_by_email,
      invoice_same_as_shipping: abonement.invoice_same_as_shipping,
      recipient_name: abonement.recipient_name,
      fullName,
      invoiceSame,
    });

    const variables = {
      // Meta
      contractNumber: displayContractNumber,
      currentDate: this._formatDateTime(new Date()),

      // Empfänger (An die) — auto-fill from shipping data if no explicit recipient set
      recipientName: abonement.recipient_name || fullName,
      recipientAddress: abonement.recipient_address || `${abonement.street || ''}, ${abonement.postal_code || ''} ${abonement.city || ''}`.trim(),

      // Shipping
      shippingCustomerClass: isCompany ? '' : 'checked',
      shippingCompanyClass: isCompany ? 'checked' : '',
      shippingFullName: fullName,
      shippingStreet: abonement.street || '',
      shippingPostalCode: abonement.postal_code || '',
      shippingCity: abonement.city || '',
      shippingPhone: abonement.phone || '',
      shippingEmail: abonement.email || '',

      // Invoice same as shipping
      invoiceSameAsShippingMark: invoiceSame ? '✓' : '',

      // Invoice address
      invoiceCompanyClass: isCompany ? 'checked' : '',
      invoiceCustomerClass: isCompany ? '' : 'checked',
      invoiceFullName: invoiceSame ? fullName : (abonement.invoice_full_name || ''),
      invoiceStreet: invoiceSame ? (abonement.street || '') : (abonement.invoice_street || ''),
      invoicePostalCode: invoiceSame ? (abonement.postal_code || '') : (abonement.invoice_postal_code || ''),
      invoiceCity: invoiceSame ? (abonement.city || '') : (abonement.invoice_city || ''),
      invoicePhone: invoiceSame ? (abonement.phone || '') : (abonement.invoice_phone || ''),
      invoiceEmail: invoiceSame ? (abonement.email || '') : (abonement.invoice_email || ''),

      // Company numbers (FN / ATU) — only shown for company users
      fnCheckedClass: isCompany && fnNumber ? 'checked' : '',
      fnNumber,
      atuCheckedClass: isCompany && atuNumber ? 'checked' : '',
      atuNumber,

      // Unternehmer / Konsument
      entrepreneurClass: isCompany ? 'checked' : '',
      consumerClass: isCompany ? '' : 'checked',

      // Product selection
      selectedProductsHtml: this._buildSelectedProductsHtml(abonement),

      // Payment
      paymentSepaClass: abonement.payment_method === 'sepa' ? 'checked' : '',
      paymentCardClass: abonement.payment_method === 'card' ? 'checked' : '',
      paymentSofortClass: abonement.payment_method === 'sofort' ? 'checked' : '',
      invoiceByEmailClass: abonement.invoice_by_email ? 'checked' : '',

      // Signatures
      profitplanetSignature,
      companyStampImage: profitplanetSignature,
      signatureImage: this._buildSignatureImgHtml(signatureDataUrl),
      signingCity: signingCity || '',
      fullName,
    };

    // DEBUG: log template source and variable keys with values
    logger.info('AboContractService:render_debug', {
      templateSource: template ? (template.includes('{{shippingFullName}}') ? 'has_placeholders' : 'NO_placeholders') : 'empty',
      templateLen: template?.length,
      variableKeys: Object.keys(variables),
      sampleValues: {
        shippingFullName: variables.shippingFullName,
        shippingStreet: variables.shippingStreet,
        shippingEmail: variables.shippingEmail,
        recipientName: variables.recipientName,
      },
    });

    const html = this._renderTemplate(template, variables, {
      rawKeys: new Set(['selectedProductsHtml', 'profitplanetSignature', 'signatureImage', 'companyStampImage']),
    });

    const pdfBuffer = await this._renderPdfFromHtml(html);

    const key = `abo/${abonement.id}/${contractKeyPart}.pdf`;
    await sharedExoscaleClient.send(new PutObjectCommand({
      Bucket: process.env.EXOSCALE_BUCKET,
      Key: key,
      Body: pdfBuffer,
      ContentType: 'application/pdf',
    }));

    const recipientEmail = abonement.email || actorUser?.email;
    if (recipientEmail) {
      const isDe = lang === 'de';
      const subject = isDe
        ? `ProfitPlanet Vertrag ${displayContractNumber}`
        : `ProfitPlanet Contract ${displayContractNumber}`;

      const text = isDe
        ? 'Ihr unterschriebener Vertrag ist als PDF im Anhang.'
        : 'Your signed contract is attached as a PDF.';

      const mailHtml = `<!doctype html>
<html>
  <head><meta charset="utf-8"></head>
  <body style="font-family:Arial,sans-serif;">
    <p>${isDe ? 'Hallo' : 'Hi'},</p>
    <p>${isDe ? 'Ihr unterschriebener Vertrag ist als PDF im Anhang.' : 'Your signed contract is attached as a PDF.'}</p>
    <p>${isDe ? 'Vertragsnummer' : 'Contract number'}: <strong>${this._escapeHtml(displayContractNumber)}</strong></p>
    <p>${isDe ? 'Viele Grüße' : 'Best regards'},<br>ProfitPlanet</p>
  </body>
</html>`;

      const mailPayload = {
        subject,
        text,
        html: mailHtml,
        lang,
        attachments: [{
          name: `${contractKeyPart}.pdf`,
          content: pdfBuffer.toString('base64'),
        }],
      };

      await MailService.sendAboContractEmail({ email: recipientEmail, ...mailPayload });

      // When subscription is for someone else, also send the contract to the purchaser
      const purchaserEmail = actorUser?.email;
      const isForSomeoneElse = isForSelf === false;
      if (isForSomeoneElse && purchaserEmail && purchaserEmail !== recipientEmail) {
        try {
          await MailService.sendAboContractEmail({ email: purchaserEmail, ...mailPayload });
        } catch (e) {
          logger.warn('AboContractService:purchaser_email_failed', { purchaserEmail, message: e?.message });
        }
      }
    } else {
      logger.warn('AboContractService:missing_recipient_email', { abonementId: abonement.id });
    }

    return { storageKey: key, contractNumber: displayContractNumber };
  }

  /**
   * Generate sequential contract number: ABO-YYYY-NNNNN
   * Uses MAX(id) from coffee_abonements as a simple sequence.
   */
  async _generateSequentialContractNumber() {
    const year = new Date().getFullYear();
    const [rows] = await pool.query(
      `SELECT COALESCE(MAX(id), 0) + 1 AS next_seq FROM coffee_abonements`
    );
    const seq = rows[0]?.next_seq || 1;
    return `ABO-${year}-${String(seq).padStart(5, '0')}`;
  }

  /**
   * Resolve user type from actorUser JWT payload or DB lookup.
   */
  async _resolveUserType(actorUser) {
    const fromToken = actorUser?.userType || actorUser?.user_type;
    if (fromToken) return fromToken;
    if (!actorUser?.id) return 'personal';
    try {
      const [rows] = await pool.query(
        `SELECT user_type FROM users WHERE id = ? LIMIT 1`,
        [actorUser.id]
      );
      return rows[0]?.user_type || 'personal';
    } catch {
      return 'personal';
    }
  }

  /**
   * Load company-specific data (FN number, ATU) for a company user.
   */
  async _loadCompanyData(userId) {
    try {
      const [rows] = await pool.query(
        `SELECT registration_number, atu_number FROM company_profiles WHERE user_id = ? LIMIT 1`,
        [userId]
      );
      return {
        registrationNumber: rows[0]?.registration_number || '',
        atuNumber: rows[0]?.atu_number || '',
      };
    } catch {
      return { registrationNumber: '', atuNumber: '' };
    }
  }
}

module.exports = AboContractService;
