const CompanyStampRepository = require('../../../repositories/stamp/CompanyStampRepository');
const UnitOfWork = require('../../../database/UnitOfWork');
const { logger } = require('../../../middleware/logger');

const ALLOWED_MIME = new Set(['image/png', 'image/jpeg', 'image/webp']);
const MAX_IMAGE_BYTES = 500 * 1024; // 500 KB
const MAX_BASE64_LENGTH = Math.ceil((MAX_IMAGE_BYTES / 3) * 4) + 16; // safety
const STAMP_CACHE_TTL_MS = 5 * 60 * 1000;
const _cache = new Map(); // companyId -> { stamp, ts }
const PRIMARY_COMPANY_ID = 1; // Our own company (single stamp policy)

function stripDataUri(b64, providedMime) {
  if (!b64) return { pure: '', mime: providedMime };
  const m = /^data:([\w/+.-]+);base64,(.+)$/i.exec(b64);
  if (m) return { mime: m[1], pure: m[2] };
  return { pure: b64, mime: providedMime };
}

function validateBase64(str) {
  return /^[A-Za-z0-9+/=\r\n]+$/.test(str);
}

function decodeSizeApprox(base64) {
  const cleaned = base64.replace(/[\r\n]/g, '');
  const len = cleaned.length;
  if (!len) return 0;
  const padding = (cleaned.endsWith('==') ? 2 : cleaned.endsWith('=') ? 1 : 0);
  return (len * 3) / 4 - padding;
}

class CompanyStampService {
  async uploadStamp({ user, base64, mimeType, label, activate = false }) {
    if (!user || user.user_type !== 'company')
      throw new Error('Only company users can upload stamps');

    const companyId = user.id || user.userId;

    // Enforce single-stamp rule for primary company BEFORE any mutation
    if (companyId === PRIMARY_COMPANY_ID) {
      const existingPrimary = await CompanyStampRepository.findByCompanyId(PRIMARY_COMPANY_ID);
      if (existingPrimary && existingPrimary.length) {
        const err = new Error('Primary company stamp already exists');
        err.code = 'PRIMARY_STAMP_EXISTS';
        err.existing = existingPrimary[0];
        throw err; // controller will translate to preview response
      }
    }

    const { pure, mime } = stripDataUri(base64, mimeType);
    const finalMime = mime || mimeType;
    if (!ALLOWED_MIME.has(finalMime)) throw new Error('Unsupported MIME type');
    if (!validateBase64(pure)) throw new Error('Invalid base64 data');
    if (pure.length > MAX_BASE64_LENGTH) throw new Error('Image too large (base64 length)');
    const bytes = decodeSizeApprox(pure);
    if (bytes > MAX_IMAGE_BYTES) throw new Error('Image exceeds size limit');

    const uow = new UnitOfWork();
    await uow.start();
    try {
      // default inactive until optionally activated
      const created = await CompanyStampRepository.create(
        {
          company_id: companyId,
          label,
          mime_type: finalMime,
          image_base64: pure,
          is_active: !!activate // treat activate flag directly (single stamp ok)
        },
        uow.connection
      );

      if (activate) {
        // For primary company single stamp: no need to deactivate; for others keep old logic
        if (companyId !== PRIMARY_COMPANY_ID) {
          await CompanyStampRepository.deactivateAllForCompany(companyId, uow.connection);
          await CompanyStampRepository.activate(created.id, companyId, uow.connection);
          created.is_active = true;
        }
        _cache.delete(companyId);
      }

      await uow.commit();
      logger.info('CompanyStampService.uploadStamp:success', { id: created.id, activate, companyId });
      return created;
    } catch (e) {
      await uow.rollback(e);
      throw e;
    }
  }

  async listCompanyStamps(user) {
    if (!user || user.user_type !== 'company')
      throw new Error('Forbidden');
    return CompanyStampRepository.findByCompanyId(user.id || user.userId);
  }

  async getActiveStampForCompany(companyId) {
    const cached = _cache.get(companyId);
    const now = Date.now();
    if (cached && now - cached.ts < STAMP_CACHE_TTL_MS) return cached.stamp;

    const stamp = await CompanyStampRepository.findActiveByCompanyId(companyId);
    _cache.set(companyId, { stamp, ts: now });
    return stamp;
  }

  async getMyActive(user) {
    if (!user || user.user_type !== 'company')
      throw new Error('Forbidden');
    return this.getActiveStampForCompany(user.id || user.userId);
  }

  async activateStamp(id, user) {
    if (!user || user.user_type !== 'company')
      throw new Error('Forbidden');
    const companyId = user.id || user.userId;
    const uow = new UnitOfWork();
    await uow.start();
    try {
      const stamp = await CompanyStampRepository.findById(id, uow.connection);
      if (!stamp || stamp.company_id !== companyId) throw new Error('Not found');
      await CompanyStampRepository.deactivateAllForCompany(companyId, uow.connection);
      await CompanyStampRepository.activate(id, companyId, uow.connection);
      await uow.commit();
      _cache.delete(companyId);
      return { ...stamp, is_active: true };
    } catch (e) {
      await uow.rollback(e);
      throw e;
    }
  }

  async deleteStamp(id, user) {
    if (!user || user.user_type !== 'company')
      throw new Error('Forbidden');
    const ok = await CompanyStampRepository.delete(id, user.id || user.userId);
    if (ok) _cache.delete(user.id || user.userId);
    return ok;
  }

  async getProfitPlanetStamp() {
    // Tries cache (reuse normal cache key)
    const cached = _cache.get(PRIMARY_COMPANY_ID);
    const now = Date.now();
    if (cached && now - cached.ts < STAMP_CACHE_TTL_MS) return cached.stamp;
    // Accept active first; if none (single stamp not active), just any
    let stamp = await CompanyStampRepository.findActiveByCompanyId(PRIMARY_COMPANY_ID);
    if (!stamp) {
      const all = await CompanyStampRepository.findByCompanyId(PRIMARY_COMPANY_ID);
      stamp = all && all[0] ? all[0] : null;
    }
    _cache.set(PRIMARY_COMPANY_ID, { stamp, ts: now });
    return stamp;
  }

  // Convenience for placeholder
  async getProfitPlanetSignatureTag(sizes = { maxW: 300, maxH: 300 }) {
    const stamp = await this.getProfitPlanetStamp();
    if (!stamp) return '';
    return `<img src="data:${stamp.mime_type};base64,${stamp.image_base64}" style="max-width:${sizes.maxW}px;max-height:${sizes.maxH}px;">`;
  }

  async listAllStamps(user) {
    if (!user || !['admin', 'super_admin'].includes(user.role)) {
      throw new Error('Forbidden');
    }
    const uow = new UnitOfWork();
    await uow.start();
    try {
      const [rows] = await uow.connection.query(`
        SELECT id, company_id, label, mime_type, image_base64, is_active, created_at, updated_at
        FROM company_stamps
        ORDER BY company_id, created_at DESC
      `);
      await uow.commit();

      // Ensure plain JSON objects with all fields and base64 string
      const plain = rows.map(r => ({
        id: r.id,
        company_id: r.company_id,
        label: r.label,
        mime_type: r.mime_type,
        image_base64:
          typeof r.image_base64 === 'string'
            ? r.image_base64
            : r.image_base64
              ? Buffer.isBuffer(r.image_base64)
                ? r.image_base64.toString('base64')
                : String(r.image_base64)
              : '',
        is_active: !!r.is_active,
        created_at: r.created_at,
        updated_at: r.updated_at
      }));

      return plain;
    } catch (e) {
      await uow.rollback(e);
      throw e;
    }
  }
}

module.exports = new CompanyStampService();
