import { Injectable, inject, DOCUMENT, effect } from '@angular/core';
import { Meta, Title } from '@angular/platform-browser';
import { Item } from '../models';
import { getDiscountedPrice, getMainImage } from '../utils/item.utils';
import { UiRuntimeFacade } from '../facades/runtime/ui-runtime.facade';
import { ConfigService } from '../core/config/config.service';
import { LanguageService } from './language.service';

const OG_LOCALE_MAP: Record<string, string> = {
  ru: 'ru_RU',
  en: 'en_US',
  hy: 'hy_AM',
};

@Injectable({
  providedIn: 'root'
})
export class SeoService {
  private meta = inject(Meta);
  private title = inject(Title);
  private doc = inject(DOCUMENT);
  private readonly uiRuntime = inject(UiRuntimeFacade);
  private readonly configService = inject(ConfigService);
  private readonly languageService = inject(LanguageService);

  constructor() {
    // Keep the runtime <title>/OG/Twitter/canonical/robots tags in sync with
    // the tenant's own bootstrap.seo.default + branding config whenever it
    // (re)loads - the static defaults in index.html are only a pre-JS /
    // no-JS-crawler fallback and are otherwise stuck on the hardcoded
    // "Marketplace" placeholder tenant. Item-detail pages override these via
    // setItemMeta() and restore them via resetToDefaults() on navigating away.
    effect(() => {
      this.configService.bootstrapRevision();
      if (this.configService.getBootstrapSnapshot()) {
        this.resetToDefaults();
      }
    });
  }

  private get siteUrl(): string {
    return this.doc?.location?.origin ?? '';
  }

  private get siteName(): string {
    return this.uiRuntime.marketplaceDisplayName() || 'Marketplace';
  }

  private get ogLocale(): string {
    return OG_LOCALE_MAP[this.languageService.currentLanguage()] ?? 'en_US';
  }

  /**
   * Set Open Graph & Twitter Card meta tags for a product/item page.
   */
  setItemMeta(item: Item): void {
    const price = item.discount > 0 ? getDiscountedPrice(item) : (item.price ?? 0);
    const imageUrl = this.resolveUrl(getMainImage(item));
    const itemUrl = `${this.siteUrl}/item/${item.itemID}`;
    const description = this.truncate(this.stripHtml(item.description || ''), 160);
    const titleText = `${item.name || 'Product'} — ${this.siteName}`;

    this.title.setTitle(titleText);
    this.setCanonical(itemUrl);

    this.setOrUpdate([
      // Open Graph
      { property: 'og:type', content: 'product' },
      { property: 'og:title', content: item.name },
      { property: 'og:description', content: description },
      { property: 'og:image', content: imageUrl },
      { property: 'og:url', content: itemUrl },
      { property: 'og:site_name', content: this.siteName },
      { property: 'og:locale', content: this.ogLocale },

      // Product-specific OG tags
      { property: 'product:price:amount', content: price.toFixed(2) },
      { property: 'product:price:currency', content: item.currency || 'RUB' },

      // Twitter Card
      { name: 'twitter:card', content: 'summary_large_image' },
      { name: 'twitter:title', content: item.name },
      { name: 'twitter:description', content: description },
      { name: 'twitter:image', content: imageUrl },

      // Standard meta
      { name: 'description', content: description },
    ]);

    this.setJsonLd({
      '@context': 'https://schema.org',
      '@type': 'Product',
      name: item.name,
      description,
      image: imageUrl,
      url: itemUrl,
      offers: {
        '@type': 'Offer',
        price: price.toFixed(2),
        priceCurrency: item.currency || 'RUB',
        availability: (item.quantity ?? 0) > 0 ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
        url: itemUrl,
      },
    });
  }

  /**
   * Reset meta tags back to the tenant's own site-wide defaults (call on
   * navigation away from an item page, and also applied automatically
   * whenever the bootstrap config (re)loads - see constructor).
   *
   * Reads `bootstrap.seo.default` (title/description/canonicalUrl/robots/
   * metaTags - editable in the Project Editor's General/Branding sections,
   * previously authored but never actually applied anywhere) and
   * `bootstrap.branding` (name/logo, for the OG/Twitter image) so the runtime
   * tags reflect the real tenant instead of a hardcoded "Marketplace"
   * placeholder. Falls back to generic copy only if bootstrap hasn't loaded
   * yet or a field is unset.
   */
  resetToDefaults(): void {
    const bootstrap = this.configService.getBootstrapSnapshot();
    const seoDefault = bootstrap?.seo?.default;
    const branding = bootstrap?.branding;

    const defaultTitle = seoDefault?.title || `${this.siteName} — Marketplace`;
    const defaultDescription = seoDefault?.description
      || 'Modern marketplace for buying digital goods. Wide selection, convenient search, fast delivery.';
    const defaultImage = this.resolveUrl(branding?.socialImageUrl || branding?.appIconUrl || branding?.logoUrl || '')
      || `${this.siteUrl}/icons/icon-512x512.png`;
    const canonicalUrl = seoDefault?.canonicalUrl ? this.resolveUrl(seoDefault.canonicalUrl) : this.siteUrl;

    this.title.setTitle(defaultTitle);
    this.setCanonical(canonicalUrl);

    this.setOrUpdate([
      { property: 'og:type', content: 'website' },
      { property: 'og:title', content: defaultTitle },
      { property: 'og:description', content: defaultDescription },
      { property: 'og:image', content: defaultImage },
      { property: 'og:url', content: this.siteUrl },
      { property: 'og:site_name', content: this.siteName },
      { property: 'og:locale', content: this.ogLocale },

      { name: 'twitter:card', content: 'summary_large_image' },
      { name: 'twitter:title', content: defaultTitle },
      { name: 'twitter:description', content: defaultDescription },
      { name: 'twitter:image', content: defaultImage },

      { name: 'description', content: defaultDescription },

      ...(seoDefault?.robots ? [{ name: 'robots', content: seoDefault.robots }] : []),
      ...(seoDefault?.metaTags ?? []),
    ]);

    // Remove product-specific tags
    this.meta.removeTag("property='product:price:amount'");
    this.meta.removeTag("property='product:price:currency'");

    this.setJsonLd({
      '@context': 'https://schema.org',
      '@type': 'Organization',
      name: this.siteName,
      url: this.siteUrl,
      ...(defaultImage ? { logo: defaultImage } : {}),
    });
  }

  /** Replace (or remove, when data is null) the page's JSON-LD structured-data script tag. */
  private setJsonLd(data: Record<string, unknown> | null): void {
    const existing = this.doc.getElementById('seo-json-ld');
    existing?.remove();

    if (!data) {
      return;
    }

    const script = this.doc.createElement('script');
    script.id = 'seo-json-ld';
    script.type = 'application/ld+json';
    script.text = JSON.stringify(data);
    this.doc.head.appendChild(script);
  }

  private setOrUpdate(tags: Array<{ property?: string; name?: string; content: string }>): void {
    for (const tag of tags) {
      const selector = tag.property
        ? `property='${tag.property}'`
        : `name='${tag.name}'`;

      const existing = this.meta.getTag(selector);
      if (existing) {
        this.meta.updateTag(tag as any, selector);
      } else {
        this.meta.addTag(tag as any);
      }
    }
  }

  /** Convert relative URLs to absolute */
  private resolveUrl(url: string): string {
    if (!url || url.startsWith('http')) return url;
    return `${this.siteUrl}${url.startsWith('/') ? '' : '/'}${url}`;
  }

  /** Strip HTML tags from a string */
  private stripHtml(html: string): string {
    return html?.replace(/<[^>]*>/g, '') || '';
  }

  /** Truncate text to maxLength, adding ellipsis */
  private truncate(text: string, maxLength: number): string {
    if (!text || text.length <= maxLength) return text || '';
    return text.substring(0, maxLength - 1) + '…';
  }

  private setCanonical(url: string): void {
    this.removeCanonical();
    const link = this.doc.createElement('link');
    link.setAttribute('rel', 'canonical');
    link.setAttribute('href', url);
    this.doc.head.appendChild(link);
  }

  private removeCanonical(): void {
    const existing = this.doc.head.querySelector('link[rel="canonical"]');
    if (existing) {
      this.doc.head.removeChild(existing);
    }
  }
}
