import type { SeoInternalLink, SeoPage } from './types'

// =============================================================================
// Linking Limits
// =============================================================================

const LIMITS = {
  /** Max location pages linked from a pillar page */
  PILLAR_TO_LOCATIONS: 50,
  /** Max cluster pages linked from a pillar page */
  PILLAR_TO_CLUSTERS: 20,
  /** Max other vertical pillar pages linked from a pillar page */
  PILLAR_TO_OTHER_VERTICALS: 3,
  /** Min sibling location pages linked from a location page */
  LOCATION_SIBLINGS_MIN: 3,
  /** Max sibling location pages linked from a location page */
  LOCATION_SIBLINGS_MAX: 5,
  /** Max cross-vertical pages linked from a location page */
  LOCATION_CROSS_VERTICAL: 3,
  /** Max cluster pages linked from a location page */
  LOCATION_TO_CLUSTERS: 2,
  /** Max location pages linked from a cluster page */
  CLUSTER_TO_LOCATIONS: 3,
  /** Max related cluster pages linked from a cluster page */
  CLUSTER_TO_CLUSTERS: 3
}

// =============================================================================
// Anchor Text Generation
// =============================================================================

/**
 * Maps a vertical slug to a human-readable label.
 */
const VERTICAL_LABELS: Record<string, string> = {
  'veterinary-clinics': 'Vet Clinics',
  'auto-repair': 'Auto Repair Shops',
  'home-services': 'Home Services',
  'hvac-contractors': 'HVAC Contractors',
  'pest-control': 'Pest Control',
  'commercial-cleaning': 'Commercial Cleaning',
  'med-spas': 'Med Spas',
  'hoa-property-management': 'HOA & Property Management',
  'security-companies': 'Security Companies'
}

/**
 * Returns a human-readable label for a vertical slug.
 * Falls back to title-casing the slug if not found in the map.
 */
export function getVerticalLabel(vertical: string): string {
  return (
    VERTICAL_LABELS[vertical] ??
    vertical
      .split('-')
      .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
      .join(' ')
  )
}

/**
 * Returns a human-readable label for a city slug.
 * Capitalises each word.
 */
function getCityLabel(city: string): string {
  return city
    .split('-')
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ')
}

/**
 * Generates anchor text for a given page.
 * - Pillar:   "AI Automation for Vet Clinics"
 * - Location: "AI Automation for Vet Clinics in Irvine"
 * - Cluster:  "AI Scheduling for Vet Clinics" (use-case label + vertical)
 * - Other:    Uses the page title directly
 */
export function generateAnchorText(page: SeoPage): string {
  const verticalLabel = getVerticalLabel(page.vertical)

  if (page.pageType === 'pillar') {
    return `AI Automation for ${verticalLabel}`
  }

  if (page.pageType === 'location' && page.city) {
    const cityLabel = getCityLabel(page.city)
    return `AI Automation for ${verticalLabel} in ${cityLabel}`
  }

  if ((page.pageType === 'cluster' || page.pageType === 'use-case') && page.useCase) {
    const useCaseLabel = page.useCase
      .split('-')
      .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
      .join(' ')
    return `${useCaseLabel} for ${verticalLabel}`
  }

  // Fallback: use the page's own title
  return page.title
}

// =============================================================================
// Helpers
// =============================================================================

/**
 * Builds a SeoInternalLink for a target page.
 */
function linkTo(target: SeoPage): SeoInternalLink {
  return {
    slug: target.slug,
    anchor_text: generateAnchorText(target)
  }
}

/**
 * Deterministically shuffles an array using the source page ID as a seed.
 * This keeps output stable across multiple runs for the same source page.
 */
function seededShuffle<T>(arr: T[], seed: string): T[] {
  // Simple numeric hash of the seed string
  let hash = 0
  for (let i = 0; i < seed.length; i++) {
    hash = (hash * 31 + seed.charCodeAt(i)) >>> 0
  }

  const copy = [...arr]
  for (let i = copy.length - 1; i > 0; i--) {
    hash = (hash * 1664525 + 1013904223) >>> 0
    const j = hash % (i + 1)
    ;[copy[i], copy[j]] = [copy[j]!, copy[i]!]
  }
  return copy
}

// =============================================================================
// Core Logic
// =============================================================================

/**
 * Generates internal links for a pillar page.
 *
 * Links to:
 * - All location pages for this vertical
 * - All cluster/use-case pages for this vertical
 * - Up to PILLAR_TO_OTHER_VERTICALS other vertical pillar pages
 */
function generatePillarLinks(page: SeoPage, allPages: SeoPage[]): SeoInternalLink[] {
  const links: SeoInternalLink[] = []

  // Location pages for this vertical
  const locationPages = allPages.filter(
    (p) => p.pageType === 'location' && p.vertical === page.vertical && p.slug !== page.slug
  )
  links.push(...locationPages.slice(0, LIMITS.PILLAR_TO_LOCATIONS).map(linkTo))

  // Cluster / use-case pages for this vertical
  const clusterPages = allPages.filter(
    (p) =>
      (p.pageType === 'cluster' || p.pageType === 'use-case') && p.vertical === page.vertical && p.slug !== page.slug
  )
  links.push(...clusterPages.slice(0, LIMITS.PILLAR_TO_CLUSTERS).map(linkTo))

  // Other vertical pillar pages (different vertical, same page type)
  const otherPillarPages = allPages.filter((p) => p.pageType === 'pillar' && p.vertical !== page.vertical)
  const shuffled = seededShuffle(otherPillarPages, page.id)
  links.push(...shuffled.slice(0, LIMITS.PILLAR_TO_OTHER_VERTICALS).map(linkTo))

  return links
}

/**
 * Generates internal links for a location page.
 *
 * Links to:
 * - Parent pillar page (same vertical)
 * - 3–5 sibling location pages (same vertical, different city)
 * - Up to LOCATION_CROSS_VERTICAL cross-vertical pages (different vertical, same city)
 * - Up to LOCATION_TO_CLUSTERS cluster pages (same vertical)
 */
function generateLocationLinks(page: SeoPage, allPages: SeoPage[]): SeoInternalLink[] {
  const links: SeoInternalLink[] = []

  // Parent pillar page
  const pillar = allPages.find((p) => p.pageType === 'pillar' && p.vertical === page.vertical)
  if (pillar) {
    links.push(linkTo(pillar))
  }

  // Sibling location pages (same vertical, different city)
  const siblings = allPages.filter(
    (p) => p.pageType === 'location' && p.vertical === page.vertical && p.slug !== page.slug
  )
  const shuffledSiblings = seededShuffle(siblings, page.id)
  links.push(...shuffledSiblings.slice(0, LIMITS.LOCATION_SIBLINGS_MAX).map(linkTo))

  // Cross-vertical pages (different vertical, same city)
  if (page.city) {
    const crossVertical = allPages.filter(
      (p) => p.pageType === 'location' && p.vertical !== page.vertical && p.city === page.city
    )
    const shuffledCross = seededShuffle(crossVertical, page.id + '-cross')
    links.push(...shuffledCross.slice(0, LIMITS.LOCATION_CROSS_VERTICAL).map(linkTo))
  }

  // Cluster pages for this vertical
  const clusterPages = allPages.filter(
    (p) => (p.pageType === 'cluster' || p.pageType === 'use-case') && p.vertical === page.vertical
  )
  const shuffledClusters = seededShuffle(clusterPages, page.id + '-cluster')
  links.push(...shuffledClusters.slice(0, LIMITS.LOCATION_TO_CLUSTERS).map(linkTo))

  return links
}

/**
 * Generates internal links for a cluster/use-case page.
 *
 * Links to:
 * - Parent pillar page (same vertical)
 * - Up to CLUSTER_TO_LOCATIONS location pages (same vertical)
 * - Up to CLUSTER_TO_CLUSTERS related cluster pages (same vertical, different use-case)
 */
function generateClusterLinks(page: SeoPage, allPages: SeoPage[]): SeoInternalLink[] {
  const links: SeoInternalLink[] = []

  // Parent pillar page
  const pillar = allPages.find((p) => p.pageType === 'pillar' && p.vertical === page.vertical)
  if (pillar) {
    links.push(linkTo(pillar))
  }

  // Location pages for this vertical
  const locationPages = allPages.filter((p) => p.pageType === 'location' && p.vertical === page.vertical)
  const shuffledLocations = seededShuffle(locationPages, page.id)
  links.push(...shuffledLocations.slice(0, LIMITS.CLUSTER_TO_LOCATIONS).map(linkTo))

  // Related cluster pages (same vertical, different slug)
  const relatedClusters = allPages.filter(
    (p) =>
      (p.pageType === 'cluster' || p.pageType === 'use-case') && p.vertical === page.vertical && p.slug !== page.slug
  )
  const shuffledClusters = seededShuffle(relatedClusters, page.id + '-sibling')
  links.push(...shuffledClusters.slice(0, LIMITS.CLUSTER_TO_CLUSTERS).map(linkTo))

  return links
}

// =============================================================================
// Public API
// =============================================================================

/**
 * Generates internal links for a page based on the hub-and-spoke model.
 *
 * Pure function — no database access. Can be tested in isolation.
 *
 * Rules applied per page type:
 * - pillar:         → location pages + cluster pages + other vertical pillars
 * - location:       → parent pillar + siblings + cross-vertical + clusters
 * - cluster/use-case: → parent pillar + location pages + related clusters
 * - comparison/other: → empty list (no hub-and-spoke rules defined)
 *
 * @param page - The page for which to generate links
 * @param allPages - All published SEO pages (used to find related pages)
 * @returns Array of internal links sorted stable (pillar-first, then alphabetical by slug)
 */
export function generateInternalLinks(page: SeoPage, allPages: SeoPage[]): SeoInternalLink[] {
  switch (page.pageType) {
    case 'pillar':
      return generatePillarLinks(page, allPages)
    case 'location':
      return generateLocationLinks(page, allPages)
    case 'cluster':
    case 'use-case':
      return generateClusterLinks(page, allPages)
    default:
      return []
  }
}
