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

const repo = new I18nPreferencesRepository();

class I18nPreferencesController {
  static _normalizeLanguageCode(value) {
    const raw = String(value == null ? '' : value).trim();
    if (!raw) return '';

    const normalized = raw.replace('_', '-');
    const pattern = /^[a-z]{2,3}(-[a-z0-9]{2,8})?$/i;
    if (!pattern.test(normalized)) return null;
    return normalized.toLowerCase();
  }

  static _normalizeStringArray(values) {
    if (!Array.isArray(values)) return [];
    const normalized = values
      .map((v) => (v == null ? '' : String(v).trim()))
      .filter(Boolean);
    return [...new Set(normalized)];
  }

  static _normalizeCategories(categories) {
    if (!Array.isArray(categories)) return [];

    return categories
      .map((item, idx) => {
        const id = String(item?.id ?? '').trim() || `category_${idx + 1}`;
        const label = String(item?.label ?? id).trim() || id;
        const namespaces = I18nPreferencesController._normalizeStringArray(item?.namespaces);
        const isCustom = Boolean(item?.isCustom);
        return { id, label, namespaces, isCustom };
      });
  }

  static _buildResponse(preferences) {
    return {
      ok: true,
      preferences,
      categories: preferences.categories,
      globalKeys: preferences.globalKeys,
    };
  }

  static _normalizeLanguagePayload(body) {
    const source = (body?.language && typeof body.language === 'object') ? body.language : body;

    const rawLanguageCode = source?.languageCode ?? source?.code ?? source?.lang;
    const hasAnyLanguageField = rawLanguageCode !== undefined
      || source?.label !== undefined
      || source?.name !== undefined
      || source?.isEnabled !== undefined
      || source?.isCustom !== undefined;

    if (!hasAnyLanguageField) return null;

    const languageCode = I18nPreferencesController._normalizeLanguageCode(rawLanguageCode);
    if (languageCode === null || !languageCode) {
      throw new Error('Invalid language payload: languageCode is required');
    }

    const label = String(source?.label ?? source?.name ?? languageCode.toUpperCase()).trim();
    const toBool = (v, fallback) => {
      if (v === undefined || v === null) return fallback;
      if (typeof v === 'boolean') return v;
      const n = String(v).trim().toLowerCase();
      if (['1', 'true', 'yes', 'on'].includes(n)) return true;
      if (['0', 'false', 'no', 'off'].includes(n)) return false;
      return fallback;
    };

    return {
      languageCode,
      label,
      isEnabled: toBool(source?.isEnabled, true),
      isCustom: toBool(source?.isCustom, true),
    };
  }

  static _normalizeTranslationsPayload(body, fallbackLanguageCode) {
    const raw = body?.translations ?? body?.translationOverrides ?? body?.entries;
    if (raw == null) return [];

    const normalized = [];

    if (Array.isArray(raw)) {
      for (const item of raw) {
        const languageCode = I18nPreferencesController._normalizeLanguageCode(
          item?.languageCode ?? item?.lang ?? fallbackLanguageCode
        );
        const namespace = String(item?.namespace ?? '').trim();
        const key = String(item?.key ?? item?.t_key ?? '').trim();
        const value = item?.value ?? item?.t_value;
        if (!languageCode || !namespace || !key || value === undefined) continue;
        normalized.push({
          languageCode,
          namespace,
          key,
          value: String(value),
          isCustom: item?.isCustom,
        });
      }
      return normalized;
    }

    if (typeof raw === 'object') {
      for (const [namespace, kv] of Object.entries(raw)) {
        if (!kv || typeof kv !== 'object') continue;
        for (const [key, value] of Object.entries(kv)) {
          const languageCode = I18nPreferencesController._normalizeLanguageCode(fallbackLanguageCode);
          if (!languageCode || !namespace || !key || value === undefined) continue;
          normalized.push({
            languageCode,
            namespace: String(namespace).trim(),
            key: String(key).trim(),
            value: String(value),
            isCustom: true,
          });
        }
      }
    }

    return normalized;
  }

  static _extractPreferencesPayload(body) {
    const source = (body?.preferences && typeof body.preferences === 'object')
      ? body.preferences
      : body;

    const hasCategories = Object.prototype.hasOwnProperty.call(source || {}, 'categories');
    const hasGlobalKeys = Object.prototype.hasOwnProperty.call(source || {}, 'globalKeys');

    return {
      categories: hasCategories ? I18nPreferencesController._normalizeCategories(source?.categories) : undefined,
      globalKeys: hasGlobalKeys ? I18nPreferencesController._normalizeStringArray(source?.globalKeys) : undefined,
    };
  }

  static async _upsertBundle(req, res) {
    try {
      const userId = req.user?.userId ?? req.user?.id ?? null;
      const language = I18nPreferencesController._normalizeLanguagePayload(req.body || {});
      const fallbackLanguageCode = language?.languageCode
        || I18nPreferencesController._normalizeLanguageCode(req.body?.languageCode ?? req.body?.lang);
      const translations = I18nPreferencesController._normalizeTranslationsPayload(req.body || {}, fallbackLanguageCode);
      const extracted = I18nPreferencesController._extractPreferencesPayload(req.body || {});

      const result = await repo.upsertBundle({
        categories: extracted.categories,
        globalKeys: extracted.globalKeys,
        language,
        translations,
        updatedByUserId: userId,
      });

      return res.status(200).json({
        ...I18nPreferencesController._buildResponse(result.preferences),
        language: result.language || null,
        translationsUpserted: result.translationsUpserted || 0,
      });
    } catch (error) {
      if (String(error?.message || '').includes('Invalid language payload')) {
        return res.status(400).json({ ok: false, message: error.message });
      }
      logger.error('i18nPreferences:upsertBundle:failed', { error: error?.message });
      return res.status(500).json({ ok: false, message: 'Failed to upsert i18n language data' });
    }
  }

  static async get(req, res) {
    try {
      const preferences = await repo.get();
      return res.status(200).json(I18nPreferencesController._buildResponse(preferences));
    } catch (error) {
      logger.error('i18nPreferences:get:failed', { error: error?.message });
      return res.status(500).json({ ok: false, message: 'Failed to load i18n preferences' });
    }
  }

  static async post(req, res) {
    return I18nPreferencesController._upsertBundle(req, res);
  }

  static async put(req, res) {
    return I18nPreferencesController._upsertBundle(req, res);
  }

  static async delete(req, res) {
    try {
      const requestedLanguageCode = I18nPreferencesController._normalizeLanguageCode(req.query?.languageCode);
      if (requestedLanguageCode === null) {
        return res.status(400).json({ ok: false, message: 'Invalid languageCode query parameter' });
      }

      if (requestedLanguageCode) {
        const result = await repo.deleteLanguageEntries(
          requestedLanguageCode,
          req.user?.userId ?? req.user?.id ?? null
        );

        return res.status(200).json({
          ok: true,
          languageCode: requestedLanguageCode,
          deletedRows: result.deletedRows,
          touchedTables: result.touchedTables,
        });
      }

      const hasBodyReplacement = req.body && (
        Object.prototype.hasOwnProperty.call(req.body, 'categories') ||
        Object.prototype.hasOwnProperty.call(req.body, 'globalKeys')
      );

      const preferences = hasBodyReplacement
        ? await repo.upsert({
            categories: I18nPreferencesController._normalizeCategories(req.body?.categories),
            globalKeys: I18nPreferencesController._normalizeStringArray(req.body?.globalKeys),
            updatedByUserId: req.user?.userId ?? req.user?.id ?? null,
          })
        : await repo.clear(req.user?.userId ?? req.user?.id ?? null);

      return res.status(200).json(I18nPreferencesController._buildResponse(preferences));
    } catch (error) {
      logger.error('i18nPreferences:delete:failed', { error: error?.message });
      return res.status(500).json({ ok: false, message: 'Failed to delete i18n preferences' });
    }
  }

  static async getTranslations(req, res) {
    try {
      const languageCodeRaw = req.query?.languageCode ?? req.query?.lang;
      const normalizedLanguageCode = languageCodeRaw
        ? I18nPreferencesController._normalizeLanguageCode(languageCodeRaw)
        : '';
      if (normalizedLanguageCode === null) {
        return res.status(400).json({ ok: false, message: 'Invalid languageCode query parameter' });
      }

      const namespace = String(req.query?.namespace ?? '').trim() || undefined;
      const translations = await repo.listTranslations({
        languageCode: normalizedLanguageCode || undefined,
        namespace,
      });

      return res.status(200).json({ ok: true, translations });
    } catch (error) {
      logger.error('i18nTranslations:get:failed', { error: error?.message });
      return res.status(500).json({ ok: false, message: 'Failed to load translations' });
    }
  }

  static async upsertTranslations(req, res) {
    try {
      const language = I18nPreferencesController._normalizeLanguagePayload(req.body || {});
      const fallbackLanguageCode = language?.languageCode
        || I18nPreferencesController._normalizeLanguageCode(req.body?.languageCode ?? req.body?.lang);
      const translations = I18nPreferencesController._normalizeTranslationsPayload(req.body || {}, fallbackLanguageCode);

      if (!translations.length) {
        return res.status(400).json({ ok: false, message: 'No valid translations provided' });
      }

      const userId = req.user?.userId ?? req.user?.id ?? null;
      const result = await repo.upsertTranslations({ translations, updatedByUserId: userId });

      if (language) {
        await repo.upsertBundle({ language, updatedByUserId: userId });
      }

      return res.status(200).json({
        ok: true,
        translationsUpserted: result.upsertedCount || 0,
      });
    } catch (error) {
      if (String(error?.message || '').includes('Invalid language payload')) {
        return res.status(400).json({ ok: false, message: error.message });
      }
      logger.error('i18nTranslations:upsert:failed', { error: error?.message });
      return res.status(500).json({ ok: false, message: 'Failed to upsert translations' });
    }
  }

  static async scan(req, res) {
    try {
      const languageCodeRaw = req.query?.languageCode ?? req.query?.lang ?? req.body?.languageCode ?? req.body?.lang;
      const normalizedLanguageCode = languageCodeRaw
        ? I18nPreferencesController._normalizeLanguageCode(languageCodeRaw)
        : '';
      if (normalizedLanguageCode === null) {
        return res.status(400).json({ ok: false, message: 'Invalid languageCode parameter' });
      }

      const summary = await repo.getScanSummary({ languageCode: normalizedLanguageCode || undefined });
      return res.status(200).json({
        ok: true,
        ...summary,
      });
    } catch (error) {
      logger.error('i18nScan:failed', { error: error?.message });
      return res.status(500).json({ ok: false, message: 'Failed to scan i18n data' });
    }
  }
}

module.exports = I18nPreferencesController;
