// Central SEO source of truth — site constants, FAQ data, JSON-LD builders.
// The FAQ array here is rendered on the landing page AND emitted as FAQPage
// structured data, so the two never drift (Google requires them to match).

import type { Metadata } from 'next';

export const SITE_URL = process.env.NEXT_PUBLIC_APP_URL ?? 'https://buildmymcpserver.com';
export const SITE_NAME = 'BuildMyMCPServer';
export const SITE_TAGLINE = 'Describe your tool. We host the MCP server.';
export const SITE_DESCRIPTION =
  'From a natural-language prompt to a hosted, OAuth 2.1-protected MCP server in 60 seconds. Streamable HTTP, ready for Claude, Cursor and ChatGPT.';

export const SEO_KEYWORDS = [
  'MCP server',
  'Model Context Protocol',
  'hosted MCP server',
  'MCP server hosting',
  'MCP server builder',
  'create MCP server',
  'deploy MCP server',
  'MCP server generator',
  'OAuth MCP server',
  'Streamable HTTP MCP',
  'Claude MCP server',
  'Cursor MCP',
  'ChatGPT connector',
  'MCP template marketplace',
];

export interface FaqItem {
  q: string;
  a: string;
}

// Rendered on the landing page and emitted as FAQPage JSON-LD — keep in sync
// by virtue of being a single export.
export const FAQ: FaqItem[] = [
  {
    q: 'What is MCP?',
    a: 'Model Context Protocol — an open standard from Anthropic for connecting AI assistants to external tools, data and APIs over a transport like Streamable HTTP.',
  },
  {
    q: 'Do I need to write code?',
    a: 'No. You describe the tool in natural language. We generate the TypeScript server, run static checks, build a Docker image and deploy it to a public OAuth-protected URL.',
  },
  {
    q: 'Which clients work?',
    a: 'Claude Desktop, Cursor, ChatGPT Custom Connectors, VS Code Copilot, Continue.dev — anything that speaks the MCP spec.',
  },
  {
    q: 'How is auth handled?',
    a: 'Every generated server is an OAuth 2.1 Resource Server. Our control plane is the Authorization Server (PKCE + Dynamic Client Registration + Resource Indicators per RFC 8707).',
  },
  {
    q: 'Can I self-host?',
    a: 'Yes. The runner is a plain Docker container; the control plane is open to BYO Postgres + Redis. See the self-hosting guide in docs.',
  },
  {
    q: 'What about secrets?',
    a: 'AES-256-GCM at rest in Postgres, injected as environment variables into the runtime container. Never logged, never echoed back.',
  },
  {
    q: 'Cold starts?',
    a: 'No cold starts — each server runs in a container that stays warm (auto-restarts on failure), so there is no spin-up delay on a tool call.',
  },
  {
    q: 'Rate limits?',
    a: 'Every request is gated by OAuth 2.1 before it reaches your container. Configurable per-server rate limiting is on the roadmap.',
  },
  {
    q: 'How fast is generation?',
    a: 'Spec to image to live URL typically completes in 45-90 seconds.',
  },
  {
    q: 'Logs and metrics?',
    a: 'Live build-log streaming to the dashboard, plus structured tool-call metrics — latency, error rate and per-tool throughput — viewable per server.',
  },
  {
    q: 'What if I cancel?',
    a: 'You can export the full TypeScript source of every server you built. No vendor lock-in.',
  },
  {
    q: 'Custom domain?',
    a: 'On the roadmap. Today every server is reachable at a buildmymcpserver.com URL with TLS; bring-your-own-domain support is coming.',
  },
];

const SOFTWARE_FEATURES = [
  'Prompt to a deployed MCP server in under 60 seconds',
  'OAuth 2.1 authorization server — PKCE, Dynamic Client Registration (RFC 7591), Resource Indicators (RFC 8707)',
  'Streamable HTTP transport, compatible with Claude Desktop, Cursor, ChatGPT, VS Code Copilot and Continue.dev',
  'Every generated server runs in an isolated Docker container',
  'Customer secrets encrypted with AES-256-GCM, injected only at runtime',
  'Live build-log streaming to the dashboard',
  'Template marketplace — publish your server or fork someone else’s',
  'Full TypeScript source export, no vendor lock-in',
  'Self-hostable control plane with BYO Postgres and Redis',
];

// Enterprise is deliberately absent: its price is "Custom" and schema.org
// Offers with fabricated numeric prices are exactly what commit ee4713f purged.
const OFFERS = [
  {
    name: 'Hobby',
    price: '0',
    description: '1 server, 100k tool calls/month, BMM subdomain, community support.',
  },
  {
    name: 'Pro',
    price: '49',
    description:
      '5 servers, 1M tool calls/month, faster Claude analysis, priority build queue, email support.',
  },
  {
    name: 'Team',
    price: '199',
    description: '25 servers, 10M tool calls/month, audit log, shared Slack support.',
  },
];

/** Organization + WebSite + SoftwareApplication graph — emitted sitewide. */
export function siteJsonLd(): object {
  return {
    '@context': 'https://schema.org',
    '@graph': [
      {
        '@type': 'Organization',
        '@id': `${SITE_URL}/#organization`,
        name: SITE_NAME,
        url: SITE_URL,
        logo: { '@type': 'ImageObject', url: `${SITE_URL}/icon.svg` },
        foundingDate: '2026',
        foundingLocation: {
          '@type': 'Place',
          address: { '@type': 'PostalAddress', addressCountry: 'CH' },
        },
      },
      {
        '@type': 'WebSite',
        '@id': `${SITE_URL}/#website`,
        url: SITE_URL,
        name: SITE_NAME,
        publisher: { '@id': `${SITE_URL}/#organization` },
        description: SITE_DESCRIPTION,
        inLanguage: 'en',
      },
      {
        '@type': 'SoftwareApplication',
        '@id': `${SITE_URL}/#software`,
        name: SITE_NAME,
        applicationCategory: 'DeveloperApplication',
        operatingSystem: 'Web Browser',
        url: SITE_URL,
        inLanguage: 'en',
        description:
          'BuildMyMCPServer turns a natural-language prompt into a hosted, OAuth 2.1-protected Model Context Protocol (MCP) server. Describe the tools you need; the platform generates a TypeScript server, runs static checks, builds a Docker image and deploys it to a public Streamable HTTP endpoint that Claude, Cursor and ChatGPT can connect to. A template marketplace lets users publish and fork ready-made servers.',
        featureList: SOFTWARE_FEATURES,
        offers: OFFERS.map((o) => ({
          '@type': 'Offer',
          name: o.name,
          price: o.price,
          priceCurrency: 'EUR',
          description: o.description,
        })),
      },
    ],
  };
}

/** FAQPage structured data — emitted on the page that displays the FAQ. */
export function faqJsonLd(items: FaqItem[] = FAQ): object {
  return {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    mainEntity: items.map((item) => ({
      '@type': 'Question',
      name: item.q,
      acceptedAnswer: { '@type': 'Answer', text: item.a },
    })),
  };
}

/** TechArticle structured data for /guides/* SEO articles. */
export function articleJsonLd(opts: {
  title: string;
  description: string;
  path: string;
  datePublished: string;
  dateModified?: string;
  /**
   * Absolute or site-relative image URL. Defaults to the route's generated
   * opengraph-image (1200×630), which satisfies Google Discover's large-image
   * requirement once the route ships an opengraph-image.tsx.
   */
  image?: string;
  /** Named human author. Falls back to the Organization. */
  authorName?: string;
  wordCount?: number;
}): object {
  const image = opts.image ?? `${SITE_URL}${opts.path}/opengraph-image`;
  return {
    '@context': 'https://schema.org',
    '@type': 'TechArticle',
    headline: opts.title,
    description: opts.description,
    url: `${SITE_URL}${opts.path}`,
    mainEntityOfPage: { '@type': 'WebPage', '@id': `${SITE_URL}${opts.path}` },
    image: image.startsWith('http') ? image : `${SITE_URL}${image}`,
    datePublished: opts.datePublished,
    dateModified: opts.dateModified ?? opts.datePublished,
    inLanguage: 'en',
    author: opts.authorName
      ? { '@type': 'Person', name: opts.authorName }
      : { '@id': `${SITE_URL}/#organization` },
    publisher: { '@id': `${SITE_URL}/#organization` },
    ...(opts.wordCount ? { wordCount: opts.wordCount } : {}),
  };
}

/** BreadcrumbList structured data. Pass the trail from root to current page. */
export function breadcrumbJsonLd(trail: { name: string; path: string }[]): object {
  return {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    itemListElement: trail.map((crumb, i) => ({
      '@type': 'ListItem',
      position: i + 1,
      name: crumb.name,
      item: `${SITE_URL}${crumb.path}`,
    })),
  };
}

/** SoftwareApplication structured data for a published marketplace template. */
export function templateJsonLd(opts: {
  slug: string;
  title: string;
  description: string;
  category: string;
  tools: string[];
  author: string | null;
}): object {
  return {
    '@context': 'https://schema.org',
    '@type': 'SoftwareApplication',
    '@id': `${SITE_URL}/templates/${opts.slug}#software`,
    name: opts.title,
    description: opts.description,
    url: `${SITE_URL}/templates/${opts.slug}`,
    applicationCategory: 'DeveloperApplication',
    applicationSubCategory: 'MCP server',
    operatingSystem: 'Web Browser',
    inLanguage: 'en',
    keywords: ['MCP server', 'Model Context Protocol', opts.category],
    featureList: opts.tools,
    isAccessibleForFree: true,
    offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
    ...(opts.author ? { author: { '@type': 'Person', name: opts.author } } : {}),
    publisher: { '@id': `${SITE_URL}/#organization` },
  };
}

/**
 * Per-page metadata. `title` is a bare string so the root layout's
 * "%s | BuildMyMCPServer" template appends the brand exactly once.
 */
export function pageMetadata(opts: { title: string; description: string; path: string }): Metadata {
  const fullTitle = `${opts.title} | ${SITE_NAME}`;
  return {
    title: opts.title,
    description: opts.description,
    alternates: { canonical: opts.path },
    openGraph: {
      type: 'website',
      siteName: SITE_NAME,
      title: fullTitle,
      description: opts.description,
      url: `${SITE_URL}${opts.path}`,
    },
    twitter: {
      card: 'summary_large_image',
      title: fullTitle,
      description: opts.description,
    },
  };
}
