const CompanySettingsRepository = require('../../repositories/settings/CompanySettingsRepository');
const { logger } = require('../../middleware/logger');
const crypto = require('crypto');

const repo = new CompanySettingsRepository();

class CompanySettingsController {
  static _normalizeBase64ImageInput(value) {
    if (value === undefined) return undefined;
    if (value === null) return null;

    const stripDataUri = (str) => {
      const s = String(str || '').trim();
      if (!s) return '';
      const m = s.match(/^data:([^;]+);base64,(.*)$/i);
      return m ? (m[2] || '').trim() : s;
    };

    if (typeof value === 'string') {
      // If base64 came via urlencoded forms, '+' often becomes ' ' after decoding.
      // Restoring spaces back to '+' makes the payload usable again.
      let normalized = stripDataUri(value);
      if (normalized.includes(' ') && !normalized.includes('+')) {
        normalized = normalized.replace(/ /g, '+');
      }
      normalized = normalized.replace(/\s+/g, '');
      return normalized || null;
    }

    if (typeof value === 'object') {
      // Common Node.js JSON shape for Buffers: { type: 'Buffer', data: [..bytes..] }
      if (value && value.type === 'Buffer' && Array.isArray(value.data)) {
        try {
          const buf = Buffer.from(value.data);
          const asBase64 = buf.toString('base64');
          return asBase64 || null;
        } catch (_) {
          return undefined;
        }
      }

      // Common frontend shapes:
      // { kind: 'base64', base64: '...' }
      // { kind: 'base64', data: '...' }
      // { dataUrl: 'data:image/png;base64,...' }
      // { value: '...' }
      const candidates = [
        value.base64,
        value.data,
        value.value,
        value.content,
        value.full,
        value.raw,
        value.payload,
        value.src,
        value.b64,
        value.base64String,
        value.dataUrl,
        value.dataURL,
        value.data_uri,
        value.dataURI,
        value.uri,
      ];
      for (const c of candidates) {
        if (typeof c === 'string' && c.trim()) {
          let normalized = stripDataUri(c);
          if (normalized.includes(' ') && !normalized.includes('+')) {
            normalized = normalized.replace(/ /g, '+');
          }
          normalized = normalized.replace(/\s+/g, '');
          return normalized || null;
        }
      }

      // Fallback: scan shallow object for a base64-ish string
      const looksLikeImageBase64 = (str) => {
        const s = String(str || '').trim();
        return s.startsWith('data:image/') || s.startsWith('iVBORw0KGgo') || s.startsWith('/9j/') || s.startsWith('R0lGOD');
      };

      for (const v of Object.values(value)) {
        if (typeof v === 'string' && looksLikeImageBase64(v)) {
          let normalized = stripDataUri(v);
          if (normalized.includes(' ') && !normalized.includes('+')) {
            normalized = normalized.replace(/ /g, '+');
          }
          normalized = normalized.replace(/\s+/g, '');
          return normalized || null;
        }
        if (v && typeof v === 'object') {
          for (const vv of Object.values(v)) {
            if (typeof vv === 'string' && looksLikeImageBase64(vv)) {
              let normalized = stripDataUri(vv);
              if (normalized.includes(' ') && !normalized.includes('+')) {
                normalized = normalized.replace(/ /g, '+');
              }
              normalized = normalized.replace(/\s+/g, '');
              return normalized || null;
            }
          }
        }
      }
    }

    // Unknown shape; ignore instead of persisting garbage
    return undefined;
  }

  static async get(req, res) {
    try {
      const settings = await repo.get();
      return res.json(settings || {
        company_name: '',
        company_street: '',
        company_postal_city: '',
        company_country: '',
        qr_code_60_base64: null,
        qr_code_120_base64: null,
      });
    } catch (err) {
      return res.status(500).json({ message: 'Failed to load company settings' });
    }
  }

  static async update(req, res) {
    try {
      const body = req.body || {};

      const requestId = req.id;

      const contentType = (req.get('content-type') || '').toLowerCase();
      const contentLength = req.get('content-length') || null;
      if (contentType.includes('multipart/form-data')) {
        logger.warn('companySettings:update:multipart_not_supported', {
          requestId,
          contentType: req.get('content-type') || null,
          hint: 'Send JSON (application/json) or add multer middleware for this route.'
        });
      }
      if (Object.keys(body).length === 0 && contentLength && Number(contentLength) > 0) {
        logger.warn('companySettings:update:empty_parsed_body', {
          requestId,
          contentType: req.get('content-type') || null,
          contentLength,
          hint: 'Body parser may not match the request content-type or size limit.'
        });
      }

      const summarizeValue = (val) => {
        const t = val === null ? 'null' : Array.isArray(val) ? 'array' : typeof val;
        const summary = { type: t };
        if (t === 'string') {
          const s = String(val);
          summary.length = s.length;
          summary.hasDataUriPrefix = s.trim().toLowerCase().startsWith('data:image/');
          summary.hasWhitespace = /\s/.test(s);
          summary.startsWithPngSig = s.trim().startsWith('iVBORw0KGgo');
        } else if (t === 'array') {
          summary.length = val.length;
        } else if (t === 'object' && val) {
          summary.keys = Object.keys(val).slice(0, 30);
          if (val.type === 'Buffer' && Array.isArray(val.data)) {
            summary.bufferLike = true;
            summary.dataLength = val.data.length;
          }
        }
        return summary;
      };

      const hash12 = (s) => {
        try {
          if (typeof s !== 'string' || !s) return null;
          return crypto.createHash('sha256').update(s).digest('hex').slice(0, 12);
        } catch {
          return null;
        }
      };

      // Log request shape (not the base64 itself)
      const incoming60Raw = body.qr_code_60_base64 ?? body.qrCode60Base64;
      const incoming120Raw = body.qr_code_120_base64 ?? body.qrCode120Base64;
      if (incoming60Raw !== undefined || incoming120Raw !== undefined) {
        logger.info('companySettings:update:incoming_qr', {
          requestId,
          contentType: req.get('content-type') || null,
          contentLength,
          bodyKeys: Object.keys(body).slice(0, 50),
          qr60: summarizeValue(incoming60Raw),
          qr120: summarizeValue(incoming120Raw),
        });
      }

      // Accept both snake_case and camelCase
      const payload = {
        company_name: body.company_name ?? body.companyName,
        company_street: body.company_street ?? body.companyStreet,
        company_postal_city: body.company_postal_city ?? body.companyPostalCity,
        company_country: body.company_country ?? body.companyCountry,
        qr_code_60_base64: CompanySettingsController._normalizeBase64ImageInput(body.qr_code_60_base64 ?? body.qrCode60Base64),
        qr_code_120_base64: CompanySettingsController._normalizeBase64ImageInput(body.qr_code_120_base64 ?? body.qrCode120Base64),
      };

      // Only forward keys that were actually provided (so we don't wipe values on partial updates)
      const provided = {};
      for (const [key, value] of Object.entries(payload)) {
        if (value !== undefined) provided[key] = value;
      }

      // Debug without leaking base64
      if (incoming60Raw !== undefined && provided.qr_code_60_base64 === undefined) {
        logger.warn('companySettings:update:qr60_ignored', {
          requestId,
          incoming: summarizeValue(incoming60Raw),
          reason: 'normalize_returned_undefined_or_unrecognized_shape'
        });
      }
      if (incoming120Raw !== undefined && provided.qr_code_120_base64 === undefined) {
        logger.warn('companySettings:update:qr120_ignored', {
          requestId,
          incoming: summarizeValue(incoming120Raw),
          reason: 'normalize_returned_undefined_or_unrecognized_shape'
        });
      }

      if (provided.qr_code_60_base64 !== undefined || provided.qr_code_120_base64 !== undefined) {
        const len60 = typeof provided.qr_code_60_base64 === 'string' ? provided.qr_code_60_base64.length : null;
        const len120 = typeof provided.qr_code_120_base64 === 'string' ? provided.qr_code_120_base64.length : null;
        logger.info('companySettings:update:qr_normalized', {
          requestId,
          has60: provided.qr_code_60_base64 !== undefined,
          type60: provided.qr_code_60_base64 === null ? 'null' : typeof provided.qr_code_60_base64,
          len60,
          sha60: typeof provided.qr_code_60_base64 === 'string' ? hash12(provided.qr_code_60_base64) : null,
          has120: provided.qr_code_120_base64 !== undefined,
          type120: provided.qr_code_120_base64 === null ? 'null' : typeof provided.qr_code_120_base64,
          len120,
          sha120: typeof provided.qr_code_120_base64 === 'string' ? hash12(provided.qr_code_120_base64) : null,
        });
      }

      const updated = await repo.update(provided);

      if (updated && (provided.qr_code_60_base64 !== undefined || provided.qr_code_120_base64 !== undefined)) {
        const storedLen60 = typeof updated.qr_code_60_base64 === 'string' ? updated.qr_code_60_base64.length : null;
        const storedLen120 = typeof updated.qr_code_120_base64 === 'string' ? updated.qr_code_120_base64.length : null;
        logger.info('companySettings:update:qr_stored', {
          requestId,
          storedLen60,
          storedSha60: typeof updated.qr_code_60_base64 === 'string' ? hash12(updated.qr_code_60_base64) : null,
          storedLen120,
          storedSha120: typeof updated.qr_code_120_base64 === 'string' ? hash12(updated.qr_code_120_base64) : null,
        });
      }

      return res.json(updated);
    } catch (err) {
      logger.error('companySettings:update:failed', {
        requestId: req && req.id,
        message: err?.message,
        stack: err?.stack,
      });
      return res.status(500).json({ message: 'Failed to update company settings' });
    }
  }
}

module.exports = CompanySettingsController;
