<?php
/**
 * eca.php (v1.5)
 * Detail Web Service for eshop.eca.ir
 * - گالری: با پوشش img, data-*, picture/srcset, og:image, JSON-LD (نه از توضیحات/بنر/اینماد)
 * - توضیحات/مشخصات: selectorهای قوی + JSON-LD + متا
 * PHP 7+
 *
 * Usage:
 *   eca.php?url=https://eshop.eca.ir/....html
 */

ini_set('display_errors', 0);
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
mb_internal_encoding('UTF-8');

function respond($arr){ echo json_encode($arr, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT); exit; }
function err($msg){ respond(['ok'=>false,'error'=>$msg]); }

$url = isset($_GET['url']) ? trim($_GET['url']) : '';
if(!$url) err('invalid_or_missing_url');
if(!preg_match('~^https?://([^/]*\.)?eshop\.eca\.ir/.*~i', $url)) err('invalid_host_only_eshop.eca.ir');

$html = http_get_follow($url, 45);
if($html === null) err('fetch_failed');

$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML('<?xml encoding="utf-8"?>'.$html, LIBXML_NOWARNING|LIBXML_NOERROR);
libxml_clear_errors();
$xp = new DOMXPath($dom);

/* Canonical & Title */
$canonical = extract_canonical($xp) ?: $url;
$crumbs    = extract_breadcrumbs($html, $xp);
$name      = $crumbs ? end($crumbs) : null;
if(!$name){
  $ogt = $xp->query('//meta[@property="og:title"]/@content');
  if($ogt && $ogt->length) $name = trim($ogt->item(0)->nodeValue);
}

/* Offer */
list($price_raw, $currency, $in_stock, $stock_text) = extract_offer_micodata($xp);
$price_toman = null;
if($price_raw !== null){
  $price_toman = (mb_strtoupper($currency ?: '') === 'IRR') ? (int)round(((int)$price_raw)/10) : (int)$price_raw;
}

/* Description / Specs */
list($desc_html, $desc_text, $desc_node) = extract_description_and_node($html, $xp);
$attributes = extract_specs($xp, $desc_node);

/* Images (improved) */
$images = extract_gallery_images_v15($xp, $html, $desc_node, $url);

respond([
  'ok'               => true,
  'url'              => $canonical,
  'name'             => $name,
  'price_toman'      => $price_toman,
  'currency_source'  => $currency ?: 'IRR',
  'price_raw'        => $price_raw !== null ? (string)$price_raw : null,
  'in_stock'         => $in_stock,
  'stock'            => $stock_text ?: null,
  'images'           => $images,
  'categories'       => $crumbs,
  'attributes'       => $attributes,
  'description_html' => $desc_html ?: null,
  'description_text' => $desc_text ?: null
]);

/* ================= Helpers ================= */

function http_get_follow($url, $timeout=40){
  $ctx = stream_context_create([
    'http'=>[
      'method'=>'GET',
      'timeout'=>$timeout,
      'header'=>
        "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ".
        "(KHTML, like Gecko) Chrome/124.0 Safari/537.36\r\n".
        "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n",
      'ignore_errors'=>true
    ]
  ]);
  $html = @file_get_contents($url, false, $ctx);
  if($html === false || $html === null) return null;
  return to_utf8($html);
}
function to_utf8($s){
  if(function_exists('mb_detect_encoding')){
    $enc = mb_detect_encoding($s, 'UTF-8, ISO-8859-1, Windows-1256, Windows-1252', true);
    if($enc && strtoupper($enc)!=='UTF-8'){ $s=@mb_convert_encoding($s,'UTF-8',$enc); }
  }
  return $s;
}
function num_clean($str){
  $from=['۰','۱','۲','۳','۴','۵','۶','۷','۸','۹','٠','١','٢','٣','٤','٥','٦','٧','٨','٩',',','٫','٬',"\xE2\x80\x8C","\xE2\x80\x8B",' '];
  $to  =['0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','','','','','','',''];
  return str_replace($from,$to,$str);
}

/* Canonical */
function extract_canonical(DOMXPath $xp){
  $n = $xp->query('//link[@rel="canonical"]/@href');
  if($n && $n->length) return html_entity_decode($n->item(0)->nodeValue, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8');
  return null;
}

/* Breadcrumbs */
function extract_breadcrumbs($html, DOMXPath $xp){
  if(preg_match('#breadcrumb\s*:\s*\{\s*links\s*:\s*(\[[^\]]*\])#u', $html, $m)){
    $json = json_decode($m[1], true);
    if(is_array($json)){
      $arr = [];
      foreach($json as $it){
        $t = trim($it['title'] ?? '');
        if($t !== '') $arr[] = $t;
      }
      $arr = array_values(array_filter($arr, function($x){
        $x = mb_strtolower($x);
        return !in_array($x, ['خانه','home']);
      }));
      if($arr) return $arr;
    }
  }
  $crumbs = [];
  $items = $xp->query('//*[@itemtype="https://schema.org/BreadcrumbList"]//*[@itemtype="https://schema.org/ListItem"]');
  if($items && $items->length){
    foreach ($items as $li){
      $a = (new DOMXPath($li->ownerDocument))->query('.//*[@itemprop="item"]//*[@itemprop="name"]', $li);
      if($a && $a->length){
        $t = trim($a->item(0)->textContent);
        if($t!=='') $crumbs[] = $t;
      }
    }
    $crumbs = array_values(array_filter($crumbs, function($x){ return mb_strtolower($x) !== 'خانه'; }));
  }
  return $crumbs;
}

/* Offer */
function extract_offer_micodata(DOMXPath $xp){
  $price_raw = null; $currency = null; $in_stock = null; $stock_text = null;

  $price = $xp->query('//*[@itemprop="offers"]//*[@itemprop="price"]/@content | //meta[@itemprop="price"]/@content');
  if($price && $price->length) $price_raw = (int)num_clean($price->item(0)->nodeValue);

  $cur = $xp->query('//*[@itemprop="offers"]//*[@itemprop="priceCurrency"]/@content | //meta[@itemprop="priceCurrency"]/@content');
  if($cur && $cur->length) $currency = trim($cur->item(0)->nodeValue);

  $avail = $xp->query('//*[@itemprop="offers"]//*[@itemprop="availability"]/@content | //link[@itemprop="availability"]/@href | //meta[@itemprop="availability"]/@content');
  if($avail && $avail->length){
    $val = strtolower(trim($avail->item(0)->nodeValue));
    if(strpos($val,'instock')!==false) $in_stock = true;
    elseif(strpos($val,'outofstock')!==false) $in_stock = false;
  }

  $txt = $xp->query('//*[contains(@class,"product-availability") or contains(@class,"availability")]//span | //*[contains(@class,"availability")]/text()');
  if($txt && $txt->length){ $stock_text = trim($txt->item(0)->textContent); }

  if($price_raw === null){
    $docHtml = $xp->document->saveHTML($xp->document->documentElement);
    if(preg_match('#(?:price|قیمت)[^0-9]*([0-9,٫٬\x{200C}\x{200B}\s]{4,})#u', $docHtml, $m)){
      $price_raw = (int)num_clean($m[1]);
      if($currency === null) $currency = 'IRR';
    }
  }
  return [$price_raw, $currency, $in_stock, $stock_text];
}

/* Description & Node */
function extract_description_and_node($html, DOMXPath $xp){
  $queries = [
    '//*[@id="product-description"]',
    '//*[contains(@class,"product-description")]',
    '//*[@itemprop="description"]',
    '//*[contains(@class,"tab-content")]//*[contains(@class,"description")]',
    '//section[contains(@class,"description")]',
    '//div[contains(@class,"rte")]',
    '//*[@id="tab-description"]',
    '//*[contains(@class,"tab-pane") and (contains(@id,"description") or contains(@aria-labelledby,"description"))]',
  ];
  $parts = []; $desc_node = null;
  foreach($queries as $q){
    $nodes = $xp->query($q);
    if($nodes && $nodes->length){
      foreach($nodes as $n){
        $h = clean_html(inner_html($n));
        if(trim(strip_tags($h))!==''){
          if(!$desc_node) $desc_node = $n;
          $parts[] = $h;
        }
      }
    }
  }
  if(!empty($parts)){
    $joined = join("\n", uniq_nonempty($parts));
    $text   = trim(html_entity_decode(strip_tags(preg_replace('/\s+/u',' ', $joined)), ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8'));
    return [$joined, $text, $desc_node];
  }

  $scripts = $xp->query('//script[@type="application/ld+json"]');
  foreach($scripts as $sc){
    $blob = html_entity_decode($sc->nodeValue ?? '', ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8');
    $json = json_decode(trim($blob), true);
    if(!$json) continue;
    $nodes = is_assoc($json) ? [$json] : $json;
    foreach($nodes as $node){
      if(!is_array($node)) continue;
      if(isset($node['@type']) && (stripos(is_array($node['@type'])?implode(',',$node['@type']):$node['@type'], 'Product')!==false)){
        $desc = trim($node['description'] ?? '');
        if($desc !== '') return ['<p>'.htmlspecialchars($desc, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8').'</p>', $desc, null];
      }
      if(isset($node['@graph'])){
        foreach($node['@graph'] as $g){
          if(isset($g['@type']) && stripos(is_array($g['@type'])?implode(',',$g['@type']):$g['@type'], 'Product')!==false){
            $desc = trim($g['description'] ?? '');
            if($desc !== '') return ['<p>'.htmlspecialchars($desc, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8').'</p>', $desc, null];
          }
        }
      }
    }
  }

  $meta = $xp->query('//meta[@name="description"]/@content');
  if($meta && $meta->length){ $t = trim($meta->item(0)->nodeValue); if($t!=='') return ['<p>'.htmlspecialchars($t, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8').'</p>', $t, null]; }
  $ogd = $xp->query('//meta[@property="og:description"]/@content');
  if($ogd && $ogd->length){ $t = trim($ogd->item(0)->nodeValue); if($t!=='') return ['<p>'.htmlspecialchars($t, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8').'</p>', $t, null]; }

  return ['', '', null];
}

/* ---------- NEW: Gallery v1.5 ---------- */
function extract_gallery_images_v15(DOMXPath $xp, $html, $desc_node, $base_url){
  $imgs = [];

  // 1) از ریشه‌های گالری
  $galleryRoots = [
    '//*[contains(@class,"images-container")]',
    '//*[@id="product-images"]',
    '//*[contains(@class,"product-cover")]',
    '//*[contains(@class,"js-qv-mask")]',
    '//*[contains(@class,"product-images")]',
    '//*[contains(@class,"product-media")]',
  ];
  foreach($galleryRoots as $q){
    $rootNodes = $xp->query($q);
    if(!$rootNodes || !$rootNodes->length) continue;

    foreach($rootNodes as $root){
      // IMG و data-*
      $nodes = (new DOMXPath($root->ownerDocument))->query('.//img[@src or @data-src or @data-image-large-src or @data-zoom-image or @data-large]', $root);
      if($nodes && $nodes->length){
        foreach($nodes as $n){
          if($desc_node && is_descendant_of($n, $desc_node)) continue;
          $src = $n->getAttribute('data-image-large-src') ?: $n->getAttribute('data-zoom-image') ?: $n->getAttribute('data-large') ?: $n->getAttribute('src') ?: $n->getAttribute('data-src');
          $src = abs_url_like($src, $base_url);
          if($src && accept_gallery_image_relaxed($src)) $imgs[] = $src;

          // <img srcset=""> هم بررسی شود
          $srcset = $n->getAttribute('srcset');
          if($srcset){
            $best = pick_best_from_srcset($srcset, $base_url);
            if($best && accept_gallery_image_relaxed($best)) $imgs[] = $best;
          }
        }
      }

      // <picture><source srcset=...>
      $sources = (new DOMXPath($root->ownerDocument))->query('.//picture/source[@srcset]', $root);
      if($sources && $sources->length){
        foreach($sources as $s){
          $best = pick_best_from_srcset($s->getAttribute('srcset'), $base_url);
          if($best && accept_gallery_image_relaxed($best)) $imgs[] = $best;
        }
      }

      // لینک‌هایی که data-zoom-image دارند
      $aZoom = (new DOMXPath($root->ownerDocument))->query('.//a[@data-zoom-image or @href]', $root);
      foreach($aZoom as $a){
        $u = $a->getAttribute('data-zoom-image') ?: $a->getAttribute('href');
        $u = abs_url_like($u, $base_url);
        if($u && accept_gallery_image_relaxed($u)) $imgs[] = $u;
      }
    }
  }

  // 2) الگوی مستقیم URL ها در HTML (fallback)
  if(empty($imgs)){
    if(preg_match_all('#https?://[^"\']+\.(?:jpe?g|png|webp)#i', $html, $m)){
      foreach($m[0] as $u){
        if(accept_gallery_image_relaxed($u)) $imgs[] = $u;
      }
    }
  }

  // 3) og:image
  $og = $xp->query('//meta[@property="og:image"]/@content');
  if($og && $og->length){
    $u = abs_url_like($og->item(0)->nodeValue, $base_url);
    if($u && accept_gallery_image_relaxed($u)) $imgs[] = $u;
  }

  // 4) JSON-LD Product.image
  $scripts = $xp->query('//script[@type="application/ld+json"]');
  foreach($scripts as $sc){
    $blob = html_entity_decode($sc->nodeValue ?? '', ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8');
    $json = json_decode(trim($blob), true);
    if(!$json) continue;
    $nodes = is_assoc($json) ? [$json] : $json;
    foreach($nodes as $node){
      if(!is_array($node)) continue;
      $cands = [];
      if(isset($node['@type']) && stripos(is_array($node['@type'])?implode(',',$node['@type']):$node['@type'], 'Product')!==false){
        $cands = isset($node['image']) ? (is_array($node['image'])?$node['image']:[$node['image']]) : [];
      }
      if(isset($node['@graph'])){
        foreach($node['@graph'] as $g){
          if(isset($g['@type']) && stripos(is_array($g['@type'])?implode(',',$g['@type']):$g['@type'], 'Product')!==false){
            $arr = isset($g['image']) ? (is_array($g['image'])?$g['image']:[$g['image']]) : [];
            $cands = array_merge($cands, $arr);
          }
        }
      }
      foreach($cands as $u){
        $u = abs_url_like($u, $base_url);
        if($u && accept_gallery_image_relaxed($u)) $imgs[] = $u;
      }
    }
  }

  // یکتا + ترتیب
  $imgs = array_values(array_unique($imgs));
  return $imgs;
}

/* URL utils */
function abs_url_like($src, $base){
  $src = trim((string)$src);
  if($src==='') return '';
  if(preg_match('~^https?://~i', $src)) return $src;
  if(preg_match('~^//~',$src)){
    $sch = parse_url($base, PHP_URL_SCHEME) ?: 'https';
    return $sch.':'.$src;
  }
  $p = parse_url($base);
  if(!$p || empty($p['host'])) return $src;
  $root = ($p['scheme'] ?? 'https').'://'.$p['host'].(isset($p['port'])?':'.$p['port']:'');
  if(substr($src,0,1)==='/') return $root.$src;
  $dir = preg_replace('~/[^/]*$~','/',$p['path'] ?? '/');
  return $root.$dir.$src;
}

/* فیلتر تصاویر گالری (relaxed ولی امن) */
function accept_gallery_image_relaxed($u){
  $l = strtolower($u);

  // فقط عکس‌های محصول، نه لوگو/بنر/اینماد/ماژول/تم
  $bad_sub = ['placeholder','no-image','enamad','namad','samandehi','trust','shaparak','logo','badge','banner','/img/cms/','/themes/','/modules/','/assets/','sprite','icon','favicon','.svg'];
  foreach($bad_sub as $b){ if(strpos($l,$b)!==false) return false; }

  // میزبان: ترجیحاً خود دامنه
  // (اگر CDNs خود سایت باشند هم قبول می‌کنیم)
  // مسیرهای متعارف پرستاشاپ: /img/p/ و /img/
  if(strpos($l, '/img/') === false && strpos($l, '/media/') === false){
    // هنوز اجازه بده ولی با احتیاط (برای srcset های مطلق)
  }

  // پسوند تصویر معتبر
  if(!preg_match('~\.(jpe?g|png|webp)(\?|#|$)~i', $u)) return false;

  return true;
}

/* srcset helper: گرفتن بهترین URL */
function pick_best_from_srcset($srcset, $base){
  $best = '';
  $maxD = -1;
  foreach(explode(',', $srcset) as $part){
    $part = trim($part);
    if($part==='') continue;
    // "<url> 2x" یا "<url> 800w"
    if(preg_match('~\s+(\d+(?:\.\d+)?)(x|w)\s*$~i', $part, $m)){
      $u = trim(substr($part, 0, -strlen($m[0])));
      $d = floatval($m[1]);
    } else {
      $u = $part; $d = 1.0;
    }
    $u = abs_url_like($u, $base);
    if($d > $maxD){ $maxD = $d; $best = $u; }
  }
  return $best ?: '';
}

/* Tree util */
function is_descendant_of(DOMNode $node, DOMNode $ancestor){
  for($n=$node; $n; $n=$n->parentNode){
    if($n->isSameNode($ancestor)) return true;
  }
  return false;
}

/* Specs */
function extract_specs(DOMXPath $xp, $desc_node=null){
  $attrs = [];
  if($desc_node){
    $tables = (new DOMXPath($desc_node->ownerDocument))->query('.//table', $desc_node);
    foreach($tables as $tbl){
      $trs = $tbl->getElementsByTagName('tr');
      foreach($trs as $tr){
        $cells = [];
        foreach($tr->childNodes as $td){
          if($td->nodeType === XML_ELEMENT_NODE){
            $cells[] = trim(strip_tags(inner_html($td)));
          }
        }
        if(count($cells)>=2){
          $n = trim($cells[0]); $v = trim($cells[1]);
          if($n!=='' && $v!=='') $attrs[] = ['name'=>$n,'value'=>$v];
        }
      }
    }
  }
  if(empty($attrs)){
    $tables = $xp->query('//table');
    foreach($tables as $tbl){
      $trs = $tbl->getElementsByTagName('tr');
      foreach($trs as $tr){
        $cells = [];
        foreach($tr->childNodes as $td){
          if($td->nodeType === XML_ELEMENT_NODE){
            $cells[] = trim(strip_tags(inner_html($td)));
          }
        }
        if(count($cells)>=2){
          $n = trim($cells[0]); $v = trim($cells[1]);
          if($n!=='' && $v!=='') $attrs[] = ['name'=>$n,'value'=>$v];
        }
      }
    }
  }
  if(empty($attrs)){
    $dls = $xp->query('//dl');
    foreach($dls as $dl){
      $html = inner_html($dl);
      if(preg_match_all('#<dt[^>]*>(.*?)</dt>\s*<dd[^>]*>(.*?)</dd>#is', $html, $pairs, PREG_SET_ORDER)){
        foreach($pairs as $p){
          $n = trim(strip_tags($p[1])); $v = trim(strip_tags($p[2]));
          if($n!=='' && $v!=='') $attrs[] = ['name'=>$n,'value'=>$v];
        }
      }
    }
  }
  if(empty($attrs)){
    $uls = $xp->query('//ul');
    foreach($uls as $ul){
      $lis = $ul->getElementsByTagName('li');
      foreach($lis as $li){
        $txt = trim(preg_replace('/\s+/u',' ', strip_tags(inner_html($li))));
        if(mb_strpos($txt, ':')!==false){
          list($n,$v)=array_map('trim', explode(':', $txt, 2));
          if($n!=='' && $v!=='') $attrs[] = ['name'=>$n,'value'=>$v];
        }
      }
    }
  }
  $seen = []; $final = [];
  foreach($attrs as $a){
    $key = mb_strtolower($a['name']).'|'.mb_strtolower($a['value']);
    if(isset($seen[$key])) continue;
    $seen[$key]=1; $final[]=$a;
  }
  return $final;
}

/* HTML utils */
function inner_html(DOMNode $node){
  $doc = $node->ownerDocument; $html = '';
  foreach($node->childNodes as $child){ $html .= $doc->saveHTML($child); }
  return $html;
}
function uniq_nonempty(array $arr){
  $seen = []; $out = [];
  foreach($arr as $x){
    $t = trim(strip_tags($x));
    if($t==='') continue;
    $k = md5($t);
    if(isset($seen[$k])) continue;
    $seen[$k]=1; $out[]=$x;
  }
  return $out;
}
function is_assoc($arr){ return is_array($arr) && array_keys($arr)!==range(0,count($arr)-1); }

/* Cleaning */
function clean_html($h){
  $h = preg_replace('#<script\b[^<]*(?:(?!</script>)<[^<]*)*</script>#is', '', $h);
  $h = preg_replace('#<style\b[^<]*(?:(?!</style>)<[^<]*)*</style>#is', '', $h);
  $h = preg_replace('/\son\w+="[^"]*"/i', '', $h);
  $h = preg_replace("/\son\w+='[^']*'/i", '', $h);
  $h = preg_replace('/\s{2,}/u', ' ', $h);
  return trim($h);
}