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

/**
 * Generisk parser för okända receptsidor
 * Försöker JSON-LD först, sedan vanlig HTML-parsing
 * Denna är mer permissiv än site-specifika parsers
 */
export class GenericRecipeParser extends RecipeParser {
  private readonly logger = new Logger(GenericRecipeParser.name);
  canHandle(url: string): boolean {
    // Denna parser hanterar alltid (är fallback)
    return true;
  }

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

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

    // Försöka extrahera JSON-LD recipe data (flera script-taggar är vanligt)
    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 data found');
          return this.extractFromJsonLd(recipe, ogImage);
        }
      } catch {
        this.logger.warn('JSON-LD parsing failed');
      }
    }

    this.logger.log('No JSON-LD found, using 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 {
    const name = recipe.name || '';
    const description = recipe.description || '';

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

    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);
        }
      }
    }

    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 {
    // Försöka hitta titel
    let name = '';
    
    // Prova olika selector-mönster
    let titleMatch =
      html.match(/<h1[^>]*>([^<]+)<\/h1>/i) ||
      html.match(/<meta\s+property="og:title"\s+content="([^"]+)"/i) ||
      html.match(/<title>([^<]+)<\/title>/i);

    if (titleMatch) {
      name = titleMatch[1].trim();
    }

    // Försöka 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();
    }

    // Försöka extrahera ingredienser från vanliga strukturer
    const ingredients: Array<{ quantity: number; unit: string; name: string; note?: string }> = [];
    
    // Testa olika ingredient-selectors
    const ingredientPatterns = [
      /<li[^>]*>(.*?)<\/li>/gi,
      /<div[^>]*class="ingredient"[^>]*>(.*?)<\/div>/gi,
      /<p[^>]*class="ingredient"[^>]*>(.*?)<\/p>/gi,
    ];

    for (const pattern of ingredientPatterns) {
      let match;
      while ((match = pattern.exec(html)) !== null) {
        const parsed = this.parseIngredientLine(match[1]);
        if (parsed && parsed.name.length > 2) {
          // Undvik mycket korta ingredienser (troligen brus)
          ingredients.push(parsed);
        }
      }
      if (ingredients.length > 0) break; // Om vi hittat några, använd dessa
    }

    // Försöka hitta instruktioner
    let instructions = '';
    const instructionsPatterns = [
      /<(?:div|section)[^>]*class="[^"]*(?:instruction|method|step)[^"]*"[^>]*>(.*?)<\/(?:div|section)>/is,
      /<ol[^>]*>(.*?)<\/ol>/i,
    ];

    for (const pattern of instructionsPatterns) {
      const match = html.match(pattern);
      if (match) {
        instructions = match[1].replace(/<[^>]+>/g, '').trim();
        if (instructions.length > 10) break;
      }
    }

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