import { Logger } from '@nestjs/common';
import { RecipeParser, ParsedRecipe } from './base.parser';

/**
 * Parser för ica.se receptsidor
 * Använder JSON-LD structured data som primär källa
 */
export class IcaRecipeParser extends RecipeParser {
  private readonly logger = new Logger(IcaRecipeParser.name);
  canHandle(url: string): boolean {
    return /ica\.se\/recept/i.test(url);
  }

  parse(html: string): ParsedRecipe {
    this.logger.log('Parsing ICA recipe...');

    // Extrahera og:image för bildurl-fallback
    const ogImage = this.extractOgImage(html);

    // Försöka extrahera JSON-LD recipe data (ICA använder ofta flera script-taggar)
    const jsonLdRegex =
      /<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/gi;
    let jsonLdMatch: RegExpExecArray | null;
    while ((jsonLdMatch = jsonLdRegex.exec(html)) !== null) {
      const rawJson = jsonLdMatch[1]?.trim();
      if (!rawJson) continue;

      try {
        const parsedJson = JSON.parse(rawJson);
        const recipe = this.findRecipeInJsonLd(parsedJson);
        if (recipe) {
          this.logger.log('JSON-LD recipe found');
          return this.extractFromJsonLd(recipe, ogImage);
        }
      } catch (err) {
        this.logger.warn(`JSON-LD parsing failed: ${err}`);
      }
    }

    // Fallback: HTML parsing (sällan nödvändigt för ICA)
    this.logger.log('Falling back to HTML parsing');
    return this.parseFromHtml(html, ogImage);
  }

  private findRecipeInJsonLd(jsonData: any): any {
    if (!jsonData) return null;

    if (Array.isArray(jsonData)) {
      for (const item of jsonData) {
        const recipe = this.findRecipeInJsonLd(item);
        if (recipe) return recipe;
      }
      return null;
    }

    if (jsonData['@type'] === 'Recipe') {
      return jsonData;
    }

    if (Array.isArray(jsonData['@type']) && jsonData['@type'].includes('Recipe')) {
      return jsonData;
    }

    const graph = jsonData['@graph'];
    if (Array.isArray(graph)) {
      return graph.find(
        (item: any) =>
          item?.['@type'] === 'Recipe' ||
          (Array.isArray(item?.['@type']) && item['@type'].includes('Recipe')),
      ) ?? null;
    }

    return null;
  }

  private extractOgImage(html: string): string | undefined {
    const match = html.match(/<meta[^>]+property="og:image"[^>]+content="([^"]+)"/i)
      || html.match(/<meta[^>]+content="([^"]+)"[^>]+property="og:image"/i);
    return match ? this.decodeHtmlEntities(match[1].trim()) : undefined;
  }

  private decodeHtmlEntities(value: string): string {
    return value
      .replace(/&amp;/g, '&')
      .replace(/&quot;/g, '"')
      .replace(/&#39;/g, "'")
      .replace(/&lt;/g, '<')
      .replace(/&gt;/g, '>');
  }

  private extractImageValue(image: any): string | undefined {
    if (!image) return undefined;
    if (typeof image === 'string') return image;

    if (Array.isArray(image)) {
      for (const item of image) {
        const extracted = this.extractImageValue(item);
        if (extracted) return extracted;
      }
      return undefined;
    }

    if (typeof image === 'object') {
      return (
        image.url ||
        image['@id'] ||
        image.contentUrl ||
        image.thumbnailUrl ||
        this.extractImageValue(image.image)
      );
    }

    return undefined;
  }

  private extractFromJsonLd(recipe: any, ogImage?: string): ParsedRecipe {
    // Extrahera titel
    const name = recipe.name || '';

    // Extrahera beskrivning
    const description = recipe.description || '';

    // Extrahera bildurl från JSON-LD (kan vara sträng eller array)
    let imageUrl: string | undefined = ogImage;
    const extractedImage = this.extractImageValue(recipe.image);
    if (extractedImage) {
      imageUrl = this.decodeHtmlEntities(extractedImage);
    }

    // Extrahera ingredienser
    const ingredients: Array<{ quantity: number; unit: string; name: string; note?: string }> = [];
    if (recipe.recipeIngredient && Array.isArray(recipe.recipeIngredient)) {
      for (const ing of recipe.recipeIngredient) {
        const parsed = this.parseIngredientLine(ing);
        if (parsed) {
          ingredients.push(parsed);
        }
      }
    }

    // Extrahera instruktioner
    let instructions = '';
    if (recipe.recipeInstructions) {
      if (typeof recipe.recipeInstructions === 'string') {
        instructions = recipe.recipeInstructions;
      } else if (Array.isArray(recipe.recipeInstructions)) {
        instructions = recipe.recipeInstructions
          .map((step: any) => {
            if (typeof step === 'string') return step;
            if (step.text) return step.text;
            return '';
          })
          .filter((s: string) => s)
          .join('\n\n');
      }
    }

    return {
      name,
      description,
      ingredients,
      instructions,
      imageUrl,
    };
  }

  private parseFromHtml(html: string, ogImage?: string): ParsedRecipe {
    let name = '';
    const titleMatch = html.match(/<h1[^>]*>([^<]+)<\/h1>/i);
    if (titleMatch) {
      name = titleMatch[1].trim();
    }

    if (!name) {
      const ogTitleMatch = html.match(
        /<meta\s+property="og:title"\s+content="([^"]+)"/i
      );
      if (ogTitleMatch) {
        name = ogTitleMatch[1].trim();
      }
    }

    // Extrahera beskrivning från meta-taggar
    let description = '';
    const descMatch = html.match(
      /<meta\s+name="description"\s+content="([^"]+)"/i
    );
    if (descMatch) {
      description = descMatch[1].trim();
    }

    const ingredients: Array<{ quantity: number; unit: string; name: string; note?: string }> = [];
    const ingredientRegex =
      /<li[^>]*class="[^"]*ingredient[^"]*"[^>]*>([^<]+)<\/li>/gi;
    let match;
    while ((match = ingredientRegex.exec(html)) !== null) {
      const parsed = this.parseIngredientLine(match[1]);
      if (parsed) {
        ingredients.push(parsed);
      }
    }

    let instructions = '';
    const instructionsMatch = html.match(
      /<(?:div|section)[^>]*class="[^"]*(?:instruction|howto)[^"]*"[^>]*>([^<]*)<\/(?:div|section)>/is
    );
    if (instructionsMatch) {
      instructions = instructionsMatch[1].replace(/<[^>]+>/g, '').trim();
    }

    return {
      name,
      description,
      ingredients,
      instructions,
      imageUrl: ogImage,
    };
  }
}
