const DocumentTemplateRepository = require('../../repositories/template/DocumentTemplateRepository');
const UnitOfWork = require('../../database/UnitOfWork');
const { logger } = require('../../middleware/logger');
const { normalizeLanguageCode } = require('../../utils/languageUtils');

const ALLOWED_USER_TYPES = new Set(['personal', 'company', 'both']);
const ALLOWED_CONTRACT_TYPES = new Set(['contract', 'gdpr', 'abo']);
const ALLOWED_INVOICE_TAX_MODES = new Set(['standard', 'reverse_charge', 'both']);

function normalizeTemplateUserType(value) {
  return ALLOWED_USER_TYPES.has(value) ? value : 'both';
}

function normalizeContractType(value) {
  return (value === undefined || value === null) ? value : String(value).trim().toLowerCase();
}

function normalizeInvoiceTaxMode(value) {
  if (value === undefined || value === null || String(value).trim() === '') return 'both';
  const normalized = String(value).trim().toLowerCase();
  return ALLOWED_INVOICE_TAX_MODES.has(normalized) ? normalized : 'both';
}

function selectTemplateByLanguage(list, requestedLanguage) {
  if (!Array.isArray(list) || !list.length) return null;

  const preferredLanguage = normalizeLanguageCode(requestedLanguage);
  const candidates = [];

  if (preferredLanguage) candidates.push(preferredLanguage);
  if (!candidates.includes('en')) candidates.push('en');

  for (const candidate of candidates) {
    const match = list.find((template) => normalizeLanguageCode(template?.lang) === candidate);
    if (match) return match;
  }

  return list[0];
}

class DocumentTemplateService {
  async listTemplates() {
    logger.info('DocumentTemplateService.listTemplates:start');
    try {
      const templates = await DocumentTemplateRepository.findAll();
      logger.info('DocumentTemplateService.listTemplates:success', { count: templates.length });
      return templates.map(t => ({
        ...t,
        lang: t.lang
      }));
    } catch (error) {
      logger.error('DocumentTemplateService.listTemplates:error', { error: error.message });
      throw error;
    }
  }

  async uploadTemplate(data) {
    logger.info('DocumentTemplateService.uploadTemplate:start', { name: data.name, type: data.type });
    const uow = new UnitOfWork();
    try {
      await uow.start();
      const rawContractType = (data.contract_type || data.contractType);
      const normalizedContractType = normalizeContractType(rawContractType);
      const user_type = normalizeTemplateUserType(data.user_type || data.userType);
      const contract_type = (data.type === 'contract' && ALLOWED_CONTRACT_TYPES.has(normalizedContractType))
        ? normalizedContractType
        : (data.type === 'contract' ? 'contract' : null);
      const tax_mode = data.type === 'invoice'
        ? normalizeInvoiceTaxMode(data.tax_mode || data.taxMode)
        : null;
      const created = await DocumentTemplateRepository.create({ ...data, user_type, contract_type, tax_mode }, uow.connection);
      await uow.commit();
      logger.info('DocumentTemplateService.uploadTemplate:success', { id: created.id });
      return created;
    } catch (err) {
      logger.error('DocumentTemplateService.uploadTemplate:error', { error: err.message });
      await uow.rollback(err);
      throw err;
    }
  }

  async getTemplate(id) {
    logger.info('DocumentTemplateService.getTemplate:start', { id });
    try {
      const template = await DocumentTemplateRepository.findById(id);
      if (!template) {
        logger.warn('DocumentTemplateService.getTemplate:not_found', { id });
        return null;
      }
      logger.debug('DocumentTemplateService.getTemplate:meta', { id, storageKey: template.storageKey, lang: template.lang, version: template.version });
      logger.info('DocumentTemplateService.getTemplate:success', { id });
      return { ...template, lang: template.lang };
    } catch (error) {
      logger.error('DocumentTemplateService.getTemplate:error', { id, error: error.message });
      throw error;
    }
  }

  async deleteTemplate(id) {
    logger.info('DocumentTemplateService.deleteTemplate:start', { id });
    const uow = new UnitOfWork();
    try {
      await uow.start();
      await DocumentTemplateRepository.delete(id, uow.connection);
      await uow.commit();
      logger.info('DocumentTemplateService.deleteTemplate:success', { id });
      return true;
    } catch (err) {
      logger.error('DocumentTemplateService.deleteTemplate:error', { id, error: err.message });
      await uow.rollback(err);
      throw err;
    }
  }

  async updateTemplate(id, data) {
    logger.info('DocumentTemplateService.updateTemplate:start', { id });
    const uow = new UnitOfWork();
    try {
      await uow.start();
      const current = await DocumentTemplateRepository.findById(id, uow.connection);
      if (!current) {
        logger.warn('DocumentTemplateService.updateTemplate:not_found', { id });
        await uow.rollback();
        return null;
      }
      const nextType = data.type !== undefined ? data.type : current.type;
      const contract_type = nextType === 'contract'
        ? (ALLOWED_CONTRACT_TYPES.has(normalizeContractType(data.contract_type || data.contractType || current.contract_type))
            ? normalizeContractType(data.contract_type || data.contractType || current.contract_type)
            : 'contract')
        : null;
      const user_type = normalizeTemplateUserType(data.user_type || data.userType || current.user_type);
      const tax_mode = nextType === 'invoice'
        ? normalizeInvoiceTaxMode(data.tax_mode || data.taxMode || current.tax_mode)
        : null;
      const newVersion = (current.version || 1) + 1;
      await DocumentTemplateRepository.update(
        id,
        { ...data, version: newVersion, user_type, contract_type, tax_mode },
        uow.connection
      );
      const updated = await DocumentTemplateRepository.findById(id, uow.connection);
      await uow.commit();
      logger.info('DocumentTemplateService.updateTemplate:success', { id, version: newVersion });
      return updated;
    } catch (err) {
      logger.error('DocumentTemplateService.updateTemplate:error', { id, error: err.message });
      await uow.rollback(err);
      throw err;
    }
  }

  async updateTemplateState(id, state) {
    logger.info('DocumentTemplateService.updateTemplateState:start', { id, state });
    const uow = new UnitOfWork();
    try {
      await uow.start();
      const current = await DocumentTemplateRepository.findById(id, uow.connection);
      if (!current) {
        logger.warn('DocumentTemplateService.updateTemplateState:not_found', { id });
        await uow.rollback();
        return null;
      }

      await DocumentTemplateRepository.updateState(id, state, uow.connection);

      // Enforce singleton active template per (contract_type + lang + overlapping user scope)
      if (state === 'active' && current.type === 'contract') {
        await DocumentTemplateRepository.deactivateOtherActiveContracts({
          excludeId: id,
          contract_type: current.contract_type || 'contract',
          lang: current.lang,
          user_type: current.user_type || 'both'
        }, uow.connection);
      }

      // Enforce singleton active invoice template per (lang + user_type)
      if (state === 'active' && current.type === 'invoice') {
        await DocumentTemplateRepository.deactivateOtherActiveInvoices({
          excludeId: id,
          lang: current.lang,
          user_type: current.user_type || 'both',
          tax_mode: current.tax_mode || 'both',
        }, uow.connection);
      }

      const updated = await DocumentTemplateRepository.findById(id, uow.connection);
      await uow.commit();
      logger.info('DocumentTemplateService.updateTemplateState:success', { id, state });
      return updated;
    } catch (err) {
      logger.error('DocumentTemplateService.updateTemplateState:error', { id, state, error: err.message });
      await uow.rollback(err);
      throw err;
    }
  }

  // NEW: Create a new template as a revision of an existing one.
  // - New record is created with version = previous.version + 1
  // - Previous record is deactivated (state -> inactive)
  // - New record state defaults to previous state (active stays active, inactive stays inactive)
  async reviseTemplate(previousId, data) {
    logger.info('DocumentTemplateService.reviseTemplate:start', { previousId });
    const uow = new UnitOfWork();
    try {
      await uow.start();
      const previous = await DocumentTemplateRepository.findById(previousId, uow.connection);
      if (!previous) {
        logger.warn('DocumentTemplateService.reviseTemplate:not_found', { previousId });
        await uow.rollback();
        return null;
      }

      const nextVersion = (previous.version || 1) + 1;
      const nextState = (data.state === 'active' || data.state === 'inactive')
        ? data.state
        : (previous.state === 'active' ? 'active' : 'inactive');
      const effectiveType = data.type || previous.type;
      const effectiveUserType = normalizeTemplateUserType(data.user_type || data.userType || previous.user_type);
      const effectiveContractType = effectiveType === 'contract'
        ? (ALLOWED_CONTRACT_TYPES.has(normalizeContractType(data.contract_type || data.contractType || previous.contract_type))
            ? normalizeContractType(data.contract_type || data.contractType || previous.contract_type)
            : 'contract')
        : null;
      const effectiveTaxMode = effectiveType === 'invoice'
        ? normalizeInvoiceTaxMode(data.tax_mode || data.taxMode || previous.tax_mode)
        : null;

      const created = await DocumentTemplateRepository.create(
        {
          ...data,
          type: effectiveType,
          user_type: effectiveUserType,
          contract_type: effectiveContractType,
          tax_mode: effectiveTaxMode,
          version: nextVersion,
          state: nextState,
        },
        uow.connection
      );

      // If new template is active and is a contract template, deactivate other conflicting actives
      if (nextState === 'active' && (data.type === 'contract' || previous.type === 'contract')) {
        await DocumentTemplateRepository.deactivateOtherActiveContracts({
          excludeId: created.id,
          contract_type: (effectiveContractType || previous.contract_type || 'contract'),
          lang: (data.lang || previous.lang),
          user_type: effectiveUserType,
        }, uow.connection);
      }

      // If new template is active and is an invoice template, deactivate other active invoices for same lang + user_type
      if (nextState === 'active' && effectiveType === 'invoice') {
        await DocumentTemplateRepository.deactivateOtherActiveInvoices({
          excludeId: created.id,
          lang: (data.lang || previous.lang),
          user_type: effectiveUserType,
          tax_mode: effectiveTaxMode,
        }, uow.connection);
      }

      // Deactivate previous (requirement: deactive the previous one)
      await DocumentTemplateRepository.updateState(previousId, 'inactive', uow.connection);

      await uow.commit();
      logger.info('DocumentTemplateService.reviseTemplate:success', { previousId, newId: created.id, version: nextVersion });
      return created;
    } catch (err) {
      logger.error('DocumentTemplateService.reviseTemplate:error', { previousId, error: err.message });
      await uow.rollback(err);
      throw err;
    }
  }

  async getActiveTemplatesForUserType(userType, templateType = null, contractType = null, taxMode = null) {
    logger.info('DocumentTemplateService.getActiveTemplatesForUserType:start', { userType, templateType, contractType, taxMode });
    try {
      const rows = await DocumentTemplateRepository.findActiveByUserType(userType, templateType, contractType, taxMode);
      logger.info('DocumentTemplateService.getActiveTemplatesForUserType:success', { count: rows.length });
      return rows.map(t => ({ ...t, lang: t.lang }));
    } catch (error) {
      logger.error('DocumentTemplateService.getActiveTemplatesForUserType:error', { error: error.message });
      throw error;
    }
  }
  
  // Convenience: return the most recent active template for a user type (by createdAt desc)
  async getLatestActiveForUserType(userType, templateType = 'contract', contractType = null, taxMode = null, lang = null) {
    logger.info('DocumentTemplateService.getLatestActiveForUserType:start', { userType, templateType, contractType, taxMode, lang });
    try {
      const list = await DocumentTemplateRepository.findActiveByUserType(userType, templateType, contractType, taxMode);
      const latest = selectTemplateByLanguage(list, lang);
      logger.info('DocumentTemplateService.getLatestActiveForUserType:result', { found: !!latest, id: latest?.id });
      return latest;
    } catch (error) {
      logger.error('DocumentTemplateService.getLatestActiveForUserType:error', { error: error.message });
      throw error;
    }
  }

  // NEW: Retrieve Profit Planet signature (company stamp) as <img> tag
  // Fallback order:
  // 1) Active stamp for provided companyId
  // 2) Any stamp for provided companyId
  // 3) Any active stamp globally
  // 4) Any stamp globally
  async getProfitPlanetSignatureTag({ companyId = null, maxW = 300, maxH = 300 } = {}) {
    const uow = new UnitOfWork();
    const result = { tag: '', reason: 'not_started' };
    logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:enter', { companyId, maxW, maxH });
    try {
      await uow.start();
      const conn = uow.connection;

      const safeRowMeta = (row) => {
        if (!row) return null;
        return {
          has_image: !!row.image_base64,
          mime: row.mime_type,
          image_len: row.image_base64 ? row.image_base64.length : 0,
          image_head: row.image_base64 ? row.image_base64.slice(0, 30) : ''
        };
      };

      // Helper to run single-row query safely
      const fetchOne = async (sql, params, reasonCode) => {
        logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:query:start', { reasonCode, sql, params });
        try {
          const started = Date.now();
          const [rows] = await conn.execute(sql, params);
          const ms = Date.now() - started;
          const rowCount = rows ? rows.length : 0;
          logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:query:result', {
            reasonCode,
            duration_ms: ms,
            rowCount,
            firstRow: safeRowMeta(rows && rows[0])
          });
          if (rows && rows[0] && rows[0].image_base64) {
            const mime = rows[0].mime_type || 'image/png';
            const dataUri = `data:${mime};base64,${rows[0].image_base64}`;
            result.tag = `<img src="${dataUri}" style="max-width:${maxW}px;max-height:${maxH}px;">`;
            result.reason = reasonCode;
            logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:query:match', {
              reasonCode,
              mime,
              dataUri_len: dataUri.length,
              dataUri_head: dataUri.slice(0, 45)
            });
            return true;
          }
        } catch (e) {
          logger.warn('DocumentTemplateService.getProfitPlanetSignatureTag:query_error', { reason: reasonCode, error: e.message });
        }
        return false;
      };

      if (companyId) {
        if (await fetchOne(
          'SELECT mime_type,image_base64 FROM company_stamps WHERE company_id = ? AND is_active = 1 ORDER BY id DESC LIMIT 1',
          [companyId],
          'company_active'
        )) { await uow.commit(); logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:exit', { reason: result.reason }); return result; }

        if (await fetchOne(
          'SELECT mime_type,image_base64 FROM company_stamps WHERE company_id = ? ORDER BY is_active DESC, id DESC LIMIT 1',
          [companyId],
          'company_any'
        )) { await uow.commit(); logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:exit', { reason: result.reason }); return result; }
      }

      if (await fetchOne(
        'SELECT mime_type,image_base64 FROM company_stamps WHERE is_active = 1 ORDER BY id DESC LIMIT 1',
        [],
        'global_active'
      )) { await uow.commit(); logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:exit', { reason: result.reason }); return result; }

      if (await fetchOne(
        'SELECT mime_type,image_base64 FROM company_stamps ORDER BY is_active DESC, id DESC LIMIT 1',
        [],
        'global_any'
      )) { await uow.commit(); logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:exit', { reason: result.reason }); return result; }

      result.reason = 'not_found';
      await uow.commit();
      logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:not_found', { companyId });
      return result;
    } catch (err) {
      result.reason = 'error';
      logger.error('DocumentTemplateService.getProfitPlanetSignatureTag:error', { error: err.message, companyId });
      try { await uow.rollback(err); } catch (rbErr) {
        logger.error('DocumentTemplateService.getProfitPlanetSignatureTag:rollback_error', { error: rbErr.message });
      }
      return result;
    } finally {
      logger.debug('DocumentTemplateService.getProfitPlanetSignatureTag:final', { reason: result.reason, hasTag: !!result.tag });
    }
  }
}

module.exports = new DocumentTemplateService();