const UnitOfWork = require('../../database/UnitOfWork');
const ContractUploadService = require('../../services/contracts/ContractUploadService');
const UserDocumentRepository = require('../../repositories/documents/UserDocumentRepository');
const { logger } = require('../../middleware/logger');
const { normalizeLanguageCode } = require('../../utils/languageUtils');

function normalizeSignature(signatureImage) {
  if (!signatureImage) return null;
  const str = String(signatureImage);
  const m = str.match(/^data:(.+);base64,(.+)$/);
  if (m && m[2]) {
    const mime = m[1] || 'image/png';
    const base64 = m[2];
    const size = Buffer.from(base64, 'base64').length;
    return { base64, mime, size };
  }
  try {
    const size = Buffer.from(str, 'base64').length;
    return { base64: str, mime: 'image/png', size };
  } catch (e) {
    logger.warn('[ContractUploadController] normalizeSignature failed to parse provided signature');
    return null;
  }
}

function resolveRequestedLanguage(contractData, user) {
  return normalizeLanguageCode(
    contractData?.lang
      ?? contractData?.language
      ?? contractData?.locale
      ?? contractData?.preferred_language
      ?? contractData?.preferredLanguage
      ?? user?.preferred_language
      ?? user?.preferredLanguage
      ?? user?.language
      ?? user?.lang
      ?? ''
  ) || null;
}

class ContractUploadController {
  static async uploadPersonalContract(req, res) {
    const userId = req.user.userId;
    logger.info(`[ContractUploadController] uploadPersonalContract called for userId: ${userId}`);
    const file = req.file; // optional, we now generate from templates when absent
    const contractData = req.body.contractData ? JSON.parse(req.body.contractData) : undefined;
    const lang = resolveRequestedLanguage(contractData, req.user);
    const signatureImage = req.body.signatureImage;

    const unitOfWork = new UnitOfWork();
    await unitOfWork.start();
    try {
      const signatureMeta = normalizeSignature(signatureImage);
      const repo = new UserDocumentRepository(unitOfWork);

      // NOTE: this DB insert is only a TEMP storage of the raw signature image.
      // The contract PDF embeds the signature from `signatureMeta.base64` during PDF generation.
      if (signatureMeta) {
        await repo.insertDocument({
          userId,
          documentType: 'signature',
          contractType: 'contract',
          objectStorageId: null,
          signatureBase64: signatureMeta.base64,
          originalFilename: 'signature.png',
          fileSize: signatureMeta.size,
          mimeType: signatureMeta.mime
        });
        logger.info('[ContractUploadController] signature stored for user', { userId, bytes: signatureMeta.size });
      }

      const uploads = [];
      const skipped = [];
      const tryUpload = async ({ contract_type, fileOverride }) => {
        try {
          const result = await ContractUploadService.uploadContract({
            userId,
            file: fileOverride,
            documentType: 'contract',
            contractCategory: 'personal',
            unitOfWork,
            contractData,
            lang,
            signatureImage: signatureMeta ? signatureMeta.base64 : null,
            contract_type,
            user_type: 'personal'
          });
          uploads.push({ contract_type, ...result });
          return true;
        } catch (e) {
          const msg = (e && e.message) ? String(e.message) : '';
          if (msg.includes('No active') && msg.includes('template found')) {
            skipped.push({ contract_type, reason: 'no_active_template' });
            logger.info('[ContractUploadController] template missing, skipping', { userId, contract_type });
            return false;
          }
          throw e;
        }
      };

      const didUploadContract = await tryUpload({ contract_type: 'contract', fileOverride: file });
      const didUploadGdpr = await tryUpload({ contract_type: 'gdpr', fileOverride: undefined });

      if (!didUploadContract && !didUploadGdpr) {
        throw new Error('No active documents are available at this moment.');
      }

      // Cleanup standalone signature record after contracts are saved
      // NOTE: deleting the signature row here DOES NOT remove it from the generated contract PDF.
      // The PDF already contains the signature pixels and is uploaded to object storage.
      if (signatureMeta) {
        await repo.deleteSignatureDocumentsForUser(userId);
        logger.info('[ContractUploadController] signature cleanup completed for user', { userId });
      }

      await unitOfWork.commit();
      logger.info(`[ContractUploadController] uploadPersonalContract success for userId: ${userId}`);
      res.json({ success: true, uploads, skipped, downloadUrls: uploads.map(u => u.url || null) });
    } catch (error) {
      logger.error(`[ContractUploadController] uploadPersonalContract error for userId: ${userId}`, { error });
      await unitOfWork.rollback(error);
      res.status(400).json({ success: false, message: error.message });
    }
  }

  static async uploadCompanyContract(req, res) {
    const userId = req.user.userId;
    logger.info(`[ContractUploadController] uploadCompanyContract called for userId: ${userId}`);
    const file = req.file;
    const contractData = req.body.contractData ? JSON.parse(req.body.contractData) : undefined;
    const lang = resolveRequestedLanguage(contractData, req.user);
    const signatureImage = req.body.signatureImage;
    const unitOfWork = new UnitOfWork();
    await unitOfWork.start();
    try {
      const signatureMeta = normalizeSignature(signatureImage);
      const repo = new UserDocumentRepository(unitOfWork);

      // NOTE: TEMP storage only; the PDF embeds the signature from `signatureMeta.base64`.
      if (signatureMeta) {
        await repo.insertDocument({
          userId,
          documentType: 'signature',
          contractType: 'contract',
          objectStorageId: null,
          signatureBase64: signatureMeta.base64,
          originalFilename: 'signature.png',
          fileSize: signatureMeta.size,
          mimeType: signatureMeta.mime
        });
        logger.info('[ContractUploadController] signature stored for company user', { userId, bytes: signatureMeta.size });
      }

      const uploads = [];
      const skipped = [];
      const tryUpload = async ({ contract_type, fileOverride }) => {
        try {
          const result = await ContractUploadService.uploadContract({
            userId,
            file: fileOverride,
            documentType: 'contract',
            contractCategory: 'company',
            unitOfWork,
            contractData,
            lang,
            signatureImage: signatureMeta ? signatureMeta.base64 : null,
            contract_type,
            user_type: 'company'
          });
          uploads.push({ contract_type, ...result });
          return true;
        } catch (e) {
          const msg = (e && e.message) ? String(e.message) : '';
          if (msg.includes('No active') && msg.includes('template found')) {
            skipped.push({ contract_type, reason: 'no_active_template' });
            logger.info('[ContractUploadController] template missing, skipping', { userId, contract_type });
            return false;
          }
          throw e;
        }
      };

      const didUploadContract = await tryUpload({ contract_type: 'contract', fileOverride: file });
      const didUploadGdpr = await tryUpload({ contract_type: 'gdpr', fileOverride: undefined });

      if (!didUploadContract && !didUploadGdpr) {
        throw new Error('No active documents are available at this moment.');
      }

      // IMPORTANT: cleanup must happen BEFORE commit() because commit closes/nulls the connection.
      // NOTE: cleanup only removes the temporary signature row, not the signature embedded in PDFs.
      if (signatureMeta) {
        await repo.deleteSignatureDocumentsForUser(userId);
        logger.info('[ContractUploadController] signature cleanup completed for company user', { userId });
      }

      await unitOfWork.commit();

      logger.info(`[ContractUploadController] uploadCompanyContract success for userId: ${userId}`);
      res.json({ success: true, uploads, skipped, downloadUrls: uploads.map(u => u.url || null) });
    } catch (error) {
      logger.error(`[ContractUploadController] uploadCompanyContract error for userId: ${userId}`, { error });
      await unitOfWork.rollback(error);
      res.status(400).json({ success: false, message: error.message });
    }
  }
}

module.exports = ContractUploadController;
