import { plans } from "@/lib/pricing";
import { siteUrl } from "@/lib/env";
import { defaultLocale } from "@/lib/i18n";
import type { BlogPost } from "@/lib/blog/types";

/**
 * schema.org nodes, centralized so cross-references stay consistent (e.g. an
 * Article's `publisher` points at the same Organization `@id` emitted globally).
 * Each function returns a plain object; render with <JsonLdScript>.
 */
export const ORG_ID = `${siteUrl}/#organization`;
const WEBSITE_ID = `${siteUrl}/#website`;
// The brand mark ships as an inline SVG component (`components/shared/logo.tsx`);
// the only raster logo asset in `public/` is this file, so it's the URL Google
// can fetch for the Knowledge Panel.
const LOGO_URL = `${siteUrl}/seo-insight-logo.webp`;
// Square brand mark; keep width/height in sync with the actual asset so the
// `ImageObject` stays Knowledge-Panel eligible.
const LOGO_WIDTH = 512;
const LOGO_HEIGHT = 512;
const DEFAULT_IMAGE = `${siteUrl}/home/dashboard-overview.webp`;

/** Brand contact email, single-sourced (matches the contact/legal pages). */
const CONTACT_EMAIL = "hello@seoinsight.app";

/**
 * Brand social profiles, single-sourced for the Organization `sameAs`.
 * TODO: confirm real social URLs — the footer currently links these icons to
 * placeholder `/` hrefs, so these are best-effort guesses pending real handles.
 */
const SOCIAL_PROFILES = [
  "https://x.com/seoinsight",
  "https://www.linkedin.com/company/seoinsight",
  "https://www.instagram.com/seoinsight",
];

/** Make a possibly-relative asset path absolute against the site URL. */
function absolute(url: string): string {
  if (url.startsWith("http")) return url;
  return `${siteUrl}${url.startsWith("/") ? "" : "/"}${url}`;
}

/** The company behind SEO Insight (SEO Insight is operated by SBInnovate Ltd). */
export function organizationNode() {
  return {
    "@type": "Organization",
    "@id": ORG_ID,
    name: "SEO Insight",
    legalName: "SBInnovate Ltd",
    url: siteUrl,
    logo: {
      "@type": "ImageObject",
      url: LOGO_URL,
      width: LOGO_WIDTH,
      height: LOGO_HEIGHT,
    },
    email: CONTACT_EMAIL,
    contactPoint: [
      {
        "@type": "ContactPoint",
        contactType: "customer support",
        email: CONTACT_EMAIL,
        availableLanguage: ["en", "fr"],
      },
    ],
    sameAs: SOCIAL_PROFILES,
    address: {
      "@type": "PostalAddress",
      streetAddress: "2 Frederick Street, Kings Cross",
      addressLocality: "London",
      postalCode: "WC1X 0ND",
      addressCountry: "GB",
    },
  };
}

export function websiteNode() {
  return {
    "@type": "WebSite",
    "@id": WEBSITE_ID,
    url: siteUrl,
    name: "SEO Insight",
    publisher: { "@id": ORG_ID },
    // Sitelinks Searchbox eligibility. The blog index is the site's primary
    // search surface (`/[lang]/blog?q=`); there is no dedicated /search route.
    potentialAction: {
      "@type": "SearchAction",
      target: {
        "@type": "EntryPoint",
        urlTemplate: `${siteUrl}/${defaultLocale}/blog?q={search_term_string}`,
      },
      "query-input": "required name=search_term_string",
    },
  };
}

/** BreadcrumbList from an ordered list of crumbs (absolute URLs). */
export function breadcrumbNode(items: { name: string; url: string }[]) {
  return {
    "@type": "BreadcrumbList",
    itemListElement: items.map((it, i) => ({
      "@type": "ListItem",
      position: i + 1,
      name: it.name,
      item: it.url,
    })),
  };
}

/**
 * Article for a resolved post, referencing the global Organization. Uses the
 * generic `Article` type (rather than the `BlogPosting` subtype) for broader
 * Google Rich Results eligibility; all properties remain valid for Article.
 */
export function blogPostingNode(post: BlogPost, lang: string) {
  const url = `${siteUrl}/${lang}${post.href}`;
  const keywords =
    post.seo?.keywords?.join(", ") ?? post.tags?.join(", ") ?? undefined;
  return {
    "@type": "Article",
    headline: post.seo?.metaTitle ?? post.title,
    description: post.seo?.metaDescription ?? post.excerpt,
    url,
    mainEntityOfPage: { "@type": "WebPage", "@id": url },
    datePublished: post.date,
    dateModified: post.updated ?? post.date,
    inLanguage: post.lang,
    image: post.seo?.ogImage ? absolute(post.seo.ogImage) : DEFAULT_IMAGE,
    author: {
      "@type": "Person",
      name: post.author.name,
      ...(post.author.linkedin || post.author.twitter
        ? {
            sameAs: [post.author.linkedin, post.author.twitter].filter(Boolean),
          }
        : {}),
    },
    publisher: { "@id": ORG_ID },
    ...(keywords ? { keywords } : {}),
  };
}

/**
 * SoftwareApplication + Offers for the product. Pricing-specific structured
 * data — only emit it on `/pricing` (not globally), so the Offer prices map to
 * the page that actually shows them. References the global Organization.
 */
export function softwareApplicationNode() {
  return {
    "@type": "SoftwareApplication",
    name: "SEO Insight",
    applicationCategory: "BusinessApplication",
    operatingSystem: "Web",
    url: siteUrl,
    publisher: { "@id": ORG_ID },
    offers: plans.map((p) => ({
      "@type": "Offer",
      name: p.name,
      price: p.monthly,
      priceCurrency: "USD",
      availability: "https://schema.org/InStock",
      // Static literal (not `new Date()`) so the emitted JSON-LD stays
      // deterministic across builds. Bump this when the price year rolls over.
      priceValidUntil: "2027-12-31",
    })),
  };
}

/**
 * Single `@graph` for the services index page: a `Service` node, a
 * `BreadcrumbList`, and (when FAQ items are passed) a `FAQPage`. Wrap in one
 * `@context` and render with <JsonLdScript>. Cross-references the global
 * Organization as the service `provider`.
 */
export function servicePageGraph(args: {
  lang: string;
  name: string;
  description: string;
  breadcrumb: { name: string; url: string }[];
  faqs?: { question: string; answer: string }[];
  path?: string;
}) {
  const {
    lang,
    name,
    description,
    breadcrumb,
    faqs,
    path = "/services",
  } = args;
  const url = `${siteUrl}/${lang}${path}`;
  const graph: object[] = [
    {
      "@type": "Service",
      name,
      description,
      url,
      serviceType: "SEO services",
      provider: { "@id": ORG_ID },
      areaServed: "Worldwide",
    },
    breadcrumbNode(breadcrumb),
  ];
  if (faqs?.length) {
    graph.push({
      "@type": "FAQPage",
      mainEntity: faqs.map((f) => ({
        "@type": "Question",
        name: f.question,
        acceptedAnswer: { "@type": "Answer", text: f.answer },
      })),
    });
  }
  return { "@context": "https://schema.org", "@graph": graph };
}
