import { Injectable, Logger } from '@nestjs/common';

export interface NormalizedFlyerItem {
  rawName: string;
  normalizedName: string;
  brand: string | null;
  categoryHint: string | null;
  price: number | null;
  priceUnit: string | null;
  comparisonPrice: number | null;
  comparisonUnit: string | null;
  weight: string | null;
  bundleWeight: string | null;
  isBundle: boolean;
  bundleItems: string[];
  offerText: string | null;
  parseConfidence: number;
  parseReasons: string[];
}

@Injectable()
export class FlyerNormalizerService {
  private readonly logger = new Logger(FlyerNormalizerService.name);
  private readonly MAX_BUNDLE_ITEMS = 20;
  private readonly MAX_BUNDLE_ITEM_LENGTH = 120;
  private readonly CHEESE_VARIANT_TO_NAME: Record<string, string> = {
    prast: 'Prästost',
    herrgard: 'Herrgårdsost',
    greve: 'Greveost',
  };

  private readonly UNIT_MAPPING: Record<string, string> = {
    // Längd
    mm: 'mm',
    cm: 'cm',
    m: 'm',
    // Vikt
    mg: 'mg',
    g: 'g',
    hg: 'hg',
    kg: 'kg',
    ton: 'ton',
    // Volym
    ml: 'ml',
    cl: 'cl',
    dl: 'dl',
    l: 'l',
    // Övrigt
    st: 'st',
    styck: 'st',
    stycke: 'st',
    pkt: 'pkt',
    paket: 'pkt',
    fp: 'pkt',
    förp: 'pkt',
    förpackning: 'pkt',
  };

  /**
   * Normaliserar en AI-parsad produktlista.
   */
  normalize(items: any[]): NormalizedFlyerItem[] {
    if (!Array.isArray(items)) {
      this.logger.warn('normalize() received non-array, returning empty list');
      return [];
    }

    return items
      .flatMap((item, idx) => this.normalizeItem(item, idx))
      .filter((item): item is NormalizedFlyerItem => item !== null);
  }

  private normalizeItem(item: any, index: number): Array<NormalizedFlyerItem | null> {
    if (!item || typeof item !== 'object') {
      this.logger.warn(`Item ${index} is not an object, skipping`);
      return [null];
    }

    const rawNameValue = this.extractString(item.rawName) || this.extractString(item.name);
    if (!rawNameValue) {
      this.logger.warn(`Item ${index} has no name, skipping`);
      return [null];
    }
    const rawName = this.fixKnownOcrTypos(rawNameValue);

    const normalizedName = this.extractString(item.normalizedName) || this.normalizeName(rawName);
    const normalizedBrand = this.normalizeBrand(this.extractString(item.brand), rawName);
    const categoryHint = this.normalizeCategory(this.extractString(item.category), rawName);
    const baseItem: NormalizedFlyerItem = {
      rawName,
      normalizedName,
      brand: normalizedBrand,
      categoryHint,
      price: this.extractPrice(item.price),
      priceUnit: this.normalizeUnit(this.extractString(item.unit)),
      comparisonPrice: this.extractPrice(item.comparisonPrice),
      comparisonUnit: this.normalizeUnit(this.extractString(item.comparisonUnit)),
      weight: this.extractString(item.weight),
      bundleWeight: this.extractString(item.bundleWeight),
      isBundle: Boolean(item.isBundle),
      bundleItems: this.extractStringArray(item.bundleItems),
      offerText: this.normalizeOfferText(item.offer),
      parseConfidence: item.confidence ?? 0.85,
      parseReasons: Array.isArray(item.reasonCodes)
        ? item.reasonCodes.map(String)
        : ['normalized'],
    };

    const expandedItems = this.expandCheeseVariants(baseItem);
    if (expandedItems.length > 0) {
      return expandedItems;
    }

    return [baseItem];
  }

  private extractString(val: any): string | null {
    if (typeof val === 'string') return val.trim() || null;
    return null;
  }

  private extractPrice(val: any): number | null {
    if (typeof val === 'number') return val;
    if (typeof val === 'string') {
      const num = parseFloat(val.replace(/,/g, '.'));
      return isFinite(num) ? num : null;
    }
    return null;
  }

  private extractStringArray(val: any): string[] {
    if (!Array.isArray(val)) return [];
    return val
      .map((entry) => String(entry).trim())
      .filter(Boolean)
      .slice(0, this.MAX_BUNDLE_ITEMS)
      .map((entry) => entry.slice(0, this.MAX_BUNDLE_ITEM_LENGTH));
  }

  private normalizeName(name: string): string {
    return name
      .toLowerCase()
      .replace(/[^a-zåäö0-9\s]/g, '')
      .replace(/\s+/g, ' ')
      .trim();
  }

  private normalizeUnit(unit: string | null): string | null {
    if (!unit) return null;

    const cleaned = unit.trim().toLowerCase().replace(/\./g, '');
    return this.UNIT_MAPPING[cleaned] ?? null;
  }

  private normalizeCategory(category: string | null, rawName?: string): string | null {
    if (this.containsSwedishCheeseVariant(rawName)) {
      return 'Hårdost';
    }
    if (!category) return null;

    const normalized = category.trim().toLowerCase();

    // Mappning av tänkta kategorivärdena från AI
    const categoryMap: Record<string, string> = {
      fisk: 'Fisk',
      kött: 'Kött',
      mejeri: 'Mejeri',
      grönsaker: 'Grönsaker',
      frukt: 'Frukt',
      dryck: 'Dryck',
      frukt_grönsaker: 'Frukt & Grönsaker',
      fastfood: 'Fastfood',
      bröd: 'Bröd',
      fryst: 'Fryst',
      godis: 'Godis',
      pasta: 'Pasta',
    };

    return categoryMap[normalized] ?? null;
  }

  private normalizeBrand(brand: string | null, rawName?: string): string | null {
    const value = `${brand ?? ''} ${rawName ?? ''}`.trim().toLowerCase();
    if (value.includes('arla ko')) {
      return 'Arla Ko';
    }
    return brand;
  }

  private containsSwedishCheeseVariant(value?: string | null): boolean {
    if (!value) return false;
    const normalized = this.stripDiacritics(value.toLowerCase());
    return ['prast', 'herrgard', 'greve'].some((token) => normalized.includes(token));
  }

  private expandCheeseVariants(item: NormalizedFlyerItem): NormalizedFlyerItem[] {
    if (item.isBundle) return [];

    const normalizedRaw = this.stripDiacritics(item.rawName.toLowerCase());
    const tokens = normalizedRaw
      .split(/[,/&]|\boch\b|\band\b/g)
      .map((part) => part.trim())
      .filter(Boolean);

    const variants = Array.from(
      new Set(
        tokens
          .map((token) => token.replace(/[^a-z0-9\s]/g, ''))
          .flatMap((token) => Object.keys(this.CHEESE_VARIANT_TO_NAME).filter((key) => token.includes(key))),
      ),
    );

    if (variants.length <= 1) {
      return [];
    }

    return variants.map((variant) => {
      const productName = this.CHEESE_VARIANT_TO_NAME[variant];
      return {
        ...item,
        rawName: productName,
        normalizedName: this.normalizeName(productName),
        categoryHint: 'Hårdost',
        parseReasons: [...item.parseReasons, 'split_cheese_variants'],
      };
    });
  }

  private stripDiacritics(value: string): string {
    return value
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '');
  }

  private normalizeOfferText(offer: any): string | null {
    if (!offer) return null;

    if (typeof offer === 'string') {
      return offer.trim() || null;
    }

    if (Array.isArray(offer)) {
      const joined = offer.map(String).filter(s => s.trim()).join(' ');
      return joined || null;
    }

    return null;
  }

  private fixKnownOcrTypos(value: string): string {
    let corrected = value;

    if (/\bbakad\b/i.test(value) && /\bfirre\b/i.test(value)) {
      corrected = corrected.replace(/\bpröd\b/gi, (match) => (match[0] === 'P' ? 'Spröd' : 'spröd'));
    }

    if (/ost\b|hårdost/i.test(value)) {
      corrected = corrected.replace(/\bherg{1,2}årds?ost\b/gi, (match) => (match[0] === 'H' ? 'Herrgårdsost' : 'herrgårdsost'));
    }

    return corrected;
  }
}
