<?php declare(strict_types = 1);

namespace MailPoet\Newsletter\Sharing;

if (!defined('ABSPATH')) exit;


use MailPoet\Entities\NewsletterEntity;
use MailPoet\Util\pQuery\pQuery;
use MailPoet\WP\Functions as WPFunctions;

class ShareMetadataBuilder {
  private const COPY_BUTTON_ATTRIBUTE = 'data-mailpoet-share-copy';

  /** @var WPFunctions */
  private $wp;

  public function __construct(
    WPFunctions $wp
  ) {
    $this->wp = $wp;
  }

  public function injectMetadata(string $html, NewsletterEntity $newsletter, string $canonicalUrl): string {
    $metadata = $this->buildMetadata($html, $newsletter, $canonicalUrl);
    $position = stripos($html, '</head>');
    if ($position === false) {
      return $metadata . "\n" . $html;
    }

    // Inject before </head> using substr_replace rather than preg_replace so
    // that dollar-digit sequences in the newsletter subject (e.g. "Save $50")
    // are not interpreted as backreferences in the replacement string.
    return substr_replace($html, $metadata . "\n", $position, 0);
  }

  /**
   * Inject the share toolbar right after <body>. Pass $replaceStateUrl when the
   * current URL carries subscriber-specific data (e.g. a tokenised view-in-browser
   * link) — the toolbar's JS will rewrite the address bar to that URL on load so
   * the subscriber's token isn't shared by accident if they copy from the bar
   * or use their browser's share menu.
   */
  public function injectShareToolbar(string $html, NewsletterEntity $newsletter, string $canonicalUrl, string $replaceStateUrl = ''): string {
    return $this->injectAfterBodyOpen($html, $this->buildShareToolbar($newsletter, $canonicalUrl, $replaceStateUrl));
  }

  public function buildMetadata(string $html, NewsletterEntity $newsletter, string $canonicalUrl): string {
    $tags = [
      $this->metaTag('name', 'robots', 'noindex, nofollow'),
    ];

    $title = $newsletter->getCampaignNameOrSubject();
    if ($title === '') {
      return implode("\n", $tags);
    }

    $description = trim($newsletter->getPreheader());
    $image = $this->findFirstContentImage($html);

    $tags[] = $this->metaTag('property', 'og:title', $title);
    $tags[] = $this->metaTag('property', 'og:type', 'website');
    $tags[] = $this->metaTag('property', 'og:url', $canonicalUrl);
    $tags[] = $this->metaTag('name', 'twitter:card', $image ? 'summary_large_image' : 'summary');
    $tags[] = $this->metaTag('name', 'twitter:title', $title);

    if ($description !== '') {
      $tags[] = $this->metaTag('property', 'og:description', $description);
      $tags[] = $this->metaTag('name', 'twitter:description', $description);
    }

    if ($image) {
      $tags[] = $this->metaTag('property', 'og:image', $image['src']);
      $tags[] = $this->metaTag('name', 'twitter:image', $image['src']);
      if ($image['alt'] !== '') {
        $tags[] = $this->metaTag('property', 'og:image:alt', $image['alt']);
      }
    }

    return implode("\n", $tags);
  }

  private function buildShareToolbar(NewsletterEntity $newsletter, string $canonicalUrl, string $replaceStateUrl): string {
    $title = __('Share this email', 'mailpoet');
    $description = __('Copy the public link or share it on your favorite channel.', 'mailpoet');
    $emailTitle = $newsletter->getCampaignNameOrSubject() ?: __('Email', 'mailpoet');
    $copyLabel = __('Copy link', 'mailpoet');
    $shareLabel = _x('Share', 'Web Share button label', 'mailpoet');
    $encodedUrl = rawurlencode($canonicalUrl);
    $encodedTitle = rawurlencode($emailTitle);

    $hostAttrs = ' data-mailpoet-share-host';
    if ($replaceStateUrl !== '') {
      $hostAttrs .= ' data-mailpoet-share-replace-state="' . $this->wp->escAttr($replaceStateUrl) . '"';
    }

    $toolbar = $this->buildShareToolbarStyles()
      . '<div class="mailpoet-share-toolbar" role="region" aria-label="' . $this->wp->escAttr($title) . '">'
      . '<div class="mailpoet-share-toolbar__inner">'
      . '<div class="mailpoet-share-toolbar__copy">'
      . '<strong>' . $this->wp->escHtml($title) . '</strong>'
      . '<span>' . $this->wp->escHtml($description) . '</span>'
      . '</div>'
      . '<div class="mailpoet-share-toolbar__controls">'
      . '<div class="mailpoet-share-toolbar__url-row">'
      . '<input class="mailpoet-share-toolbar__url components-text-control__input" type="url" readonly aria-label="' . $this->wp->escAttr(__('Public share link', 'mailpoet')) . '" value="' . $this->wp->escAttr($canonicalUrl) . '" />'
      . $this->buildShareToolbarCopyButton($canonicalUrl, $copyLabel)
      . '</div>'
      . '<div class="mailpoet-share-toolbar__actions">'
      . '<button type="button" class="mailpoet-share-toolbar__button mailpoet-share-toolbar__web-share components-button is-secondary" aria-label="' . $this->wp->escAttr($shareLabel) . '" data-mailpoet-web-share="' . $this->wp->escAttr($canonicalUrl) . '" data-mailpoet-web-share-title="' . $this->wp->escAttr($emailTitle) . '">'
      . $this->buildShareToolbarActionContent('share', $shareLabel)
      . '</button>'
      . $this->buildShareToolbarLink('facebook', $this->wp->escUrl('https://www.facebook.com/sharer/sharer.php?u=' . $encodedUrl), 'Facebook', true)
      . $this->buildShareToolbarLink('x', $this->wp->escUrl('https://twitter.com/intent/tweet?url=' . $encodedUrl . '&text=' . $encodedTitle), 'X', true)
      . $this->buildShareToolbarLink('whatsapp', $this->wp->escUrl('https://api.whatsapp.com/send?text=' . rawurlencode(trim($emailTitle . ' ' . $canonicalUrl))), 'WhatsApp', true)
      . $this->buildShareToolbarLink('email', $this->wp->escUrl('mailto:?subject=' . $encodedTitle . '&body=' . $encodedUrl), __('Email', 'mailpoet'), true)
      . '</div></div></div></div>';

    return '<div' . $hostAttrs . '>' . $toolbar . '</div>' . $this->buildShareToolbarScript();
  }

  private function buildShareToolbarCopyButton(string $canonicalUrl, string $copyLabel): string {
    return '<button type="button" class="mailpoet-share-toolbar__button mailpoet-share-toolbar__copy-button components-button is-secondary" aria-label="' . $this->wp->escAttr($copyLabel) . '" ' . self::COPY_BUTTON_ATTRIBUTE . '="' . $this->wp->escAttr($canonicalUrl) . '" data-mailpoet-share-copied-label="' . $this->wp->escAttr(__('Copied', 'mailpoet')) . '">'
      . $this->buildShareToolbarActionContent('copy', $copyLabel)
      . '</button>';
  }

  private function buildShareToolbarLink(string $iconName, string $url, string $label, bool $opensNewTab): string {
    $isSocialLink = in_array($iconName, ['facebook', 'x', 'whatsapp', 'email'], true);
    $className = $isSocialLink
      ? 'mailpoet-share-toolbar__link mailpoet-share-toolbar__social-button mailpoet-share-toolbar__social-button--' . $this->wp->escAttr($iconName) . ' components-button'
      : 'mailpoet-share-toolbar__link components-button is-secondary';

    return '<a class="' . $className . '" href="' . $url . '" aria-label="' . $this->wp->escAttr($label) . '"'
      . ($opensNewTab ? ' target="_blank" rel="noopener noreferrer"' : '')
      . '>'
      . $this->buildShareToolbarActionContent($iconName, $label)
      . '</a>';
  }

  private function buildShareToolbarActionContent(string $iconName, string $label): string {
    return $this->buildShareToolbarIcon($iconName)
      . '<span data-mailpoet-share-label aria-live="polite">' . $this->wp->escHtml($label) . '</span>';
  }

  private function buildShareToolbarIcon(string $iconName): string {
    $icons = [
      'copy' => '<path d="M16 1H4c-1.1 0-2 .9-2 2v12h2V3h12V1Zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2Zm0 16H8V7h11v14Z" />',
      'share' => '<path d="M18 16.1c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11A2.99 2.99 0 1 0 15 5c0 .24.04.47.09.7L8.04 9.81A3 3 0 1 0 8.04 14.2l7.12 4.18c-.05.2-.07.41-.07.62a2.91 2.91 0 1 0 2.91-2.9Z" />',
      'facebook' => '<path d="M14 8.5V6.75c0-.5.4-.75.85-.75H17V2.5h-3.1C10.83 2.5 9 4.33 9 7.2v1.3H6v3.75h3V22h5v-9.75h3.25l.55-3.75H14Z" />',
      'x' => '<path d="M13.7 10.6 21.4 2h-1.8l-6.7 7.5L7.6 2H1.5l8.1 11.5L1.5 22h1.8l7.1-7.8 5.7 7.8h6.1l-8.5-11.4Zm-2.5 2.8-.8-1.1L3.9 3.4h2.8l5.2 7.2.8 1.1 6.9 9h-2.8l-5.6-7.3Z" />',
      'whatsapp' => '<path d="M12.04 2C6.58 2 2.15 6.43 2.15 11.9c0 1.75.46 3.45 1.33 4.95L2 22l5.29-1.39a9.85 9.85 0 0 0 4.75 1.21h.01c5.46 0 9.9-4.43 9.9-9.89A9.9 9.9 0 0 0 12.04 2Zm5.82 14.2c-.25.7-1.45 1.34-2.02 1.43-.52.08-1.18.12-1.9-.12-.44-.14-1-.33-1.72-.64-3.02-1.3-4.99-4.33-5.14-4.53-.15-.2-1.23-1.64-1.23-3.13 0-1.49.78-2.22 1.06-2.52.28-.3.61-.37.82-.37h.59c.19 0 .44-.07.69.53.25.6.85 2.08.92 2.23.08.15.13.33.03.53-.1.2-.15.33-.3.51-.15.18-.32.4-.45.54-.15.15-.31.31-.13.61.18.3.8 1.31 1.71 2.12 1.18 1.05 2.17 1.37 2.47 1.52.3.15.48.13.66-.08.18-.2.76-.89.97-1.2.2-.3.41-.25.69-.15.28.1 1.78.84 2.08.99.3.15.51.23.58.36.08.13.08.75-.17 1.45Z" />',
      'email' => '<path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2Zm0 4-8 5-8-5V6l8 5 8-5v2Z" />',
    ];
    if (!isset($icons[$iconName])) {
      return '';
    }

    return '<span class="mailpoet-share-toolbar__icon mailpoet-share-toolbar__icon--' . $this->wp->escAttr($iconName) . '" aria-hidden="true">'
      . '<svg viewBox="0 0 24 24" focusable="false">' . $icons[$iconName] . '</svg>'
      . '</span>';
  }

  private function buildShareToolbarStyles(): string {
    return '<style>'
      . '.mailpoet-share-toolbar{background:#fff;border-bottom:1px solid #dcdcde;color:#1d2327;font-family:system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;margin-bottom:20px;position:relative;width:100%}'
      . '.mailpoet-share-toolbar *{box-sizing:border-box}'
      . '.mailpoet-share-toolbar__inner{align-items:center;display:flex;gap:24px;margin:0 auto;max-width:800px;padding:16px 24px;width:100%}'
      . '.mailpoet-share-toolbar__copy{display:flex;flex:0 0 260px;flex-direction:column;gap:4px;min-width:0}'
      . '.mailpoet-share-toolbar__copy strong{font-size:16px;font-weight:600;line-height:1.3}'
      . '.mailpoet-share-toolbar__copy span{color:#50575e;font-size:13px;line-height:1.4;text-wrap:balance}'
      . '.mailpoet-share-toolbar__controls{display:flex;flex:1 1 auto;flex-direction:column;gap:10px;min-width:0}'
      . '.mailpoet-share-toolbar__url-row{align-items:flex-end;display:flex;gap:10px}'
      . '.mailpoet-share-toolbar__url{border:1px solid #8c8f94;border-radius:2px;color:#1d2327;flex:1 1 auto;font-family:monospace;font-size:13px;line-height:20px;min-height:40px;min-width:0;padding:8px}'
      . '.mailpoet-share-toolbar__actions{display:flex;flex-wrap:wrap;gap:10px}'
      . '.mailpoet-share-toolbar__button,.mailpoet-share-toolbar__link{align-items:center;border-radius:2px;display:inline-flex;font-size:13px;font-weight:500;gap:6px;justify-content:center;line-height:20px;min-height:40px;padding:6px 12px;text-decoration:none;white-space:nowrap}'
      . '.mailpoet-share-toolbar__button.components-button.is-secondary,.mailpoet-share-toolbar__link.components-button.is-secondary{background:#fff;border:1px solid #2271b1;color:#2271b1;cursor:pointer}'
      . '.mailpoet-share-toolbar__button.components-button.is-secondary:hover,.mailpoet-share-toolbar__button.components-button.is-secondary:focus,.mailpoet-share-toolbar__link.components-button.is-secondary:hover,.mailpoet-share-toolbar__link.components-button.is-secondary:focus{border-color:#135e96;color:#135e96}'
      . '.mailpoet-share-toolbar__copy-button{flex:0 0 auto}'
      . '.mailpoet-share-toolbar__social-button.components-button{border:1px solid transparent;box-shadow:none;color:#fff;flex:1 1 0}'
      . '.mailpoet-share-toolbar__social-button.components-button:hover,.mailpoet-share-toolbar__social-button.components-button:focus{border-color:transparent;color:#fff;opacity:.85}'
      . '.mailpoet-share-toolbar__social-button--facebook.components-button{background:#1877f2}'
      . '.mailpoet-share-toolbar__social-button--x.components-button{background:#111}'
      . '.mailpoet-share-toolbar__social-button--whatsapp.components-button{background:#25d366}'
      . '.mailpoet-share-toolbar__social-button--email.components-button{background:#3858e9}'
      . '.mailpoet-share-toolbar__icon{display:inline-flex;height:20px;width:20px}'
      . '.mailpoet-share-toolbar__icon svg{display:block;fill:currentColor;height:20px;width:20px}'
      . '.mailpoet-share-toolbar__web-share{display:none}'
      . '@media(max-width:782px){.mailpoet-share-toolbar__inner{align-items:stretch;flex-direction:column;gap:12px;padding:16px}.mailpoet-share-toolbar__copy{flex:auto}.mailpoet-share-toolbar__url-row{align-items:stretch;flex-direction:column}.mailpoet-share-toolbar__button,.mailpoet-share-toolbar__link{width:100%}}'
      . '</style>';
  }

  private function buildShareToolbarScript(): string {
    return '<script>'
      . '(function(){'
      . 'var hosts=document.querySelectorAll("[data-mailpoet-share-host]");'
      // Replace state once if any host asks us to scrub a tokenised URL from the address bar.
      . 'for(var r=0;r<hosts.length;r++){var replace=hosts[r].getAttribute("data-mailpoet-share-replace-state");if(replace&&window.history&&typeof window.history.replaceState==="function"){try{window.history.replaceState(null,document.title,replace);}catch(error){}break;}}'
      . 'function copyUrl(url){'
      . 'if(navigator.clipboard&&window.isSecureContext){return navigator.clipboard.writeText(url);}'
      . 'var input=document.createElement("textarea");'
      . 'input.value=url;input.setAttribute("readonly","");input.style.position="fixed";input.style.left="-9999px";document.body.appendChild(input);input.select();'
      . 'try{document.execCommand("copy");}catch(error){}'
      . 'document.body.removeChild(input);return Promise.resolve();'
      . '}'
      . 'var copyButtons=document.querySelectorAll("[' . self::COPY_BUTTON_ATTRIBUTE . ']");'
      . 'for(var i=0;i<copyButtons.length;i++){if(copyButtons[i].dataset.mailpoetShareBound){continue;}copyButtons[i].dataset.mailpoetShareBound="1";copyButtons[i].addEventListener("click",function(){var button=this;var labelElement=button.querySelector("[data-mailpoet-share-label]");var label=labelElement?labelElement.textContent:button.textContent;copyUrl(button.getAttribute("' . self::COPY_BUTTON_ATTRIBUTE . '")).then(function(){var copiedLabel=button.getAttribute("data-mailpoet-share-copied-label")||label;if(labelElement){labelElement.textContent=copiedLabel;}else{button.textContent=copiedLabel;}window.setTimeout(function(){if(labelElement){labelElement.textContent=label;}else{button.textContent=label;}},2000);});});}'
      . 'var shareButtons=document.querySelectorAll("[data-mailpoet-web-share]");'
      . 'for(var j=0;j<shareButtons.length;j++){if(!navigator.share||shareButtons[j].dataset.mailpoetShareBound){continue;}shareButtons[j].dataset.mailpoetShareBound="1";shareButtons[j].style.display="inline-flex";shareButtons[j].addEventListener("click",function(){navigator.share({title:this.getAttribute("data-mailpoet-web-share-title"),url:this.getAttribute("data-mailpoet-web-share")}).catch(function(){});});}'
      // Promote each host into its own shadow root so email styles can\'t leak in or out.
      // Handlers were bound above on the original nodes; moving them into the shadow root preserves listeners.
      . 'for(var k=0;k<hosts.length;k++){var host=hosts[k];if(host.shadowRoot||typeof host.attachShadow!=="function"){continue;}try{var shadow=host.attachShadow({mode:"open"});while(host.firstChild){shadow.appendChild(host.firstChild);}}catch(error){}}'
      . '})();'
      . '</script>';
  }

  private function injectAfterBodyOpen(string $html, string $markup): string {
    $bodyPosition = stripos($html, '<body');
    if ($bodyPosition === false) {
      return $markup . $html;
    }

    $bodyOpeningTagEnd = strpos($html, '>', $bodyPosition);
    if ($bodyOpeningTagEnd === false) {
      return $markup . $html;
    }

    return substr($html, 0, $bodyOpeningTagEnd + 1) . $markup . substr($html, $bodyOpeningTagEnd + 1);
  }

  /**
   * @return array{src: string, alt: string}|null
   */
  private function findFirstContentImage(string $html): ?array {
    $dom = pQuery::parseStr($html);
    foreach ($dom->query('img') as $image) {
      $src = trim((string)$image->src);
      if (!$this->isUsableImage($src, $image)) {
        continue;
      }
      return [
        'src' => $src,
        'alt' => trim((string)$image->alt),
      ];
    }
    return null;
  }

  private function isUsableImage(string $src, $image): bool {
    if ($src === '' || !preg_match('/^https?:\/\//i', $src)) {
      return false;
    }

    $haystack = strtolower($src . ' ' . (string)$image->alt);
    foreach (['fake-logo', 'your-logo-placeholder', 'social-icons', 'mailpoet-logo', 'powered-by-mailpoet'] as $needle) {
      if (strpos($haystack, $needle) !== false) {
        return false;
      }
    }

    $width = isset($image->width) ? (int)$image->width : 0;
    $height = isset($image->height) ? (int)$image->height : 0;
    if (($width > 0 && $width < 64) || ($height > 0 && $height < 64)) {
      return false;
    }

    return true;
  }

  private function metaTag(string $attribute, string $name, string $content): string {
    return sprintf(
      '<meta %s="%s" content="%s" />',
      $attribute,
      $this->wp->escAttr($name),
      $this->wp->escAttr($content)
    );
  }
}
