import { siteSettings } from '~/generated/site/site'
import { loadHomePage } from '~/content/home-export'
import { loadResolvedVehicleDetail, loadVehicleListingSnapshot, resolveVehicleRoute } from '~/content/vehicles'
import {
  DEFAULT_PUBLIC_LOCALE,
  normalizePublicLocale,
  stripPublicLocalePrefix,
  withPublicLocalePrefix,
} from '~/utils/public-locales'

const LOCALE_CODES = {
  en: 'en-US',
  fr: 'fr-FR',
  es: 'es-ES',
  ru: 'ru-RU',
  de: 'de-DE',
  ka: 'ka-GE',
  it: 'it-IT',
  ar: 'ar',
  he: 'he-IL',
  pl: 'pl-PL',
  tr: 'tr-TR',
}

const DAY_CODES = {
  Mo: 'Monday',
  Tu: 'Tuesday',
  We: 'Wednesday',
  Th: 'Thursday',
  Fr: 'Friday',
  Sa: 'Saturday',
  Su: 'Sunday',
}

const DAY_ORDER = Object.keys(DAY_CODES)

function cleanObject(value) {
  if (Array.isArray(value)) {
    return value
      .map((item) => cleanObject(item))
      .filter((item) => item !== null && item !== undefined && item !== '')
  }

  if (!value || typeof value !== 'object') {
    return value
  }

  return Object.fromEntries(
    Object.entries(value)
      .map(([key, item]) => [key, cleanObject(item)])
      .filter(([, item]) => item !== null && item !== undefined && item !== '' && (!Array.isArray(item) || item.length)),
  )
}

function siteOrigin() {
  return String(siteSettings.frontendUrl || 'https://bent.ge').replace(/\/+$/, '')
}

function absoluteUrl(value, fallbackPath = '/') {
  const text = String(value || '').trim()

  if (/^https?:\/\//i.test(text)) {
    return text
  }

  const path = text || fallbackPath

  if (!path) {
    return ''
  }

  return `${siteOrigin()}${path.startsWith('/') ? path : `/${path}`}`
}

function entityUrl(entity, fallbackPath) {
  return entity?.seo?.canonical || entity?.href || absoluteUrl(entity?.path, fallbackPath)
}

function languageCode(locale) {
  return LOCALE_CODES[normalizePublicLocale(locale)] || LOCALE_CODES[DEFAULT_PUBLIC_LOCALE]
}

function nullableNumber(value) {
  if (value === null || value === undefined || value === '') {
    return null
  }

  const number = Number(value)

  return Number.isFinite(number) ? number : null
}

function offerAvailabilitySchemaUrl() {
  return 'https://schema.org/InStock'
}

function merchantReturnPolicySchema() {
  return {
    '@type': 'MerchantReturnPolicy',
    applicableCountry: 'GE',
    returnPolicyCategory: 'https://schema.org/MerchantReturnNotPermitted',
  }
}

function shippingDetailsSchema(currency = siteSettings.defaultCurrency || 'USD') {
  return {
    '@type': 'OfferShippingDetails',
    shippingDestination: {
      '@type': 'DefinedRegion',
      addressCountry: 'GE',
    },
    shippingRate: {
      '@type': 'MonetaryAmount',
      value: 0,
      currency,
    },
    deliveryTime: {
      '@type': 'ShippingDeliveryTime',
      handlingTime: {
        '@type': 'QuantitativeValue',
        minValue: 0,
        maxValue: 0,
        unitCode: 'DAY',
      },
      transitTime: {
        '@type': 'QuantitativeValue',
        minValue: 0,
        maxValue: 0,
        unitCode: 'DAY',
      },
    },
  }
}

function commerceOfferPayload(entity, url, price) {
  if (!(price > 0)) {
    return null
  }

  const currency = entity?.currency || siteSettings.defaultCurrency || 'USD'

  return {
    '@type': 'Offer',
    price,
    priceCurrency: currency,
    url,
    availability: offerAvailabilitySchemaUrl(),
    hasMerchantReturnPolicy: merchantReturnPolicySchema(),
    shippingDetails: shippingDetailsSchema(currency),
  }
}

function offerAggregateRatingSchema(entity) {
  const presentation = entity?.presentation?.homeCard || entity?.structuredContent?.presentation?.homeCard || {}
  const ratingValue = nullableNumber(entity?.rating || presentation?.rating)
  const reviewCount = nullableNumber(entity?.reviews || presentation?.reviewCount)

  if (ratingValue === null || reviewCount === null || reviewCount <= 0) {
    return null
  }

  return {
    '@type': 'AggregateRating',
    ratingValue,
    reviewCount,
  }
}

function offerReviewSchema(entity) {
  const reviewItems = []
    .concat(Array.isArray(entity?.reviews) ? entity.reviews : [])
    .concat(Array.isArray(entity?.structuredContent?.reviews) ? entity.structuredContent.reviews : [])
    .filter(Boolean)

  return reviewItems
    .map((item) => {
      const authorName = String(item?.author || item?.name || '').trim()
      const reviewBody = String(item?.text || item?.body || item?.description || '').trim()
      const ratingValue = nullableNumber(item?.rating || item?.ratingValue)

      if (!authorName || !reviewBody) {
        return null
      }

      return cleanObject({
        '@type': 'Review',
        author: {
          '@type': 'Person',
          name: authorName,
        },
        reviewBody,
        reviewRating: ratingValue !== null ? {
          '@type': 'Rating',
          ratingValue,
          bestRating: 5,
        } : null,
      })
    })
    .filter(Boolean)
}

function localizedBusiness(locale) {
  const business = siteSettings.business || {}
  const localized = business.localized?.[normalizePublicLocale(locale)] || {}

  return {
    ...business,
    ...localized,
    address: {
      ...(business.address || {}),
      ...(localized.address || {}),
    },
    geo: {
      ...(business.geo || {}),
      ...(localized.geo || {}),
    },
  }
}

function localeHomeUrl(locale) {
  const normalizedLocale = normalizePublicLocale(locale)
  const prefix = siteSettings.localeRouting?.prefixes?.[normalizedLocale] || ''

  return `${siteOrigin()}${prefix || '/'}`
}

function businessEntityId(locale) {
  return `${localeHomeUrl(locale).replace(/\/+$/, '') || siteOrigin()}/#autorental`
}

function explicitPriceRange(value) {
  const text = String(value || '').trim()

  return text && !/^\$+$/.test(text) ? text : ''
}

async function fleetPriceRange(fallbackCurrency = siteSettings.defaultCurrency || 'USD') {
  const explicitRange = explicitPriceRange(siteSettings.business?.priceRange)

  if (explicitRange) {
    return explicitRange
  }

  const vehicleListing = await loadVehicleListingSnapshot(DEFAULT_PUBLIC_LOCALE)
  const prices = (vehicleListing?.vehiclesIndex || vehicleListing?.fleetVehicles || [])
    .map((item) => ({
      price: Number(item?.priceFrom || item?.price || 0),
      currency: item?.currency || fallbackCurrency,
    }))
    .filter((item) => item.price > 0)

  if (!prices.length) {
    return ''
  }

  const currency = prices.find((item) => item.currency)?.currency || fallbackCurrency
  const values = prices.map((item) => item.price)
  const min = Math.floor(Math.min(...values))
  const max = Math.ceil(Math.max(...values))

  return min === max
    ? `${min} ${currency}`
    : `${min} ${currency} - ${max} ${currency}`
}

function expandDayRange(start, end) {
  const startIndex = DAY_ORDER.indexOf(start)
  const endIndex = DAY_ORDER.indexOf(end)

  if (startIndex === -1 || endIndex === -1) {
    return []
  }

  if (startIndex <= endIndex) {
    return DAY_ORDER.slice(startIndex, endIndex + 1).map((day) => DAY_CODES[day])
  }

  return [...DAY_ORDER.slice(startIndex), ...DAY_ORDER.slice(0, endIndex + 1)].map((day) => DAY_CODES[day])
}

function openingHoursSpecification(hours) {
  return (hours || [])
    .map((item) => {
      const match = String(item || '').trim().match(/^([A-Z][a-z])(?:-([A-Z][a-z]))?\s+(\d{2}:\d{2})-(\d{2}:\d{2})$/)

      if (!match) {
        return null
      }

      const [, startDay, endDay, opens, closes] = match

      return {
        '@type': 'OpeningHoursSpecification',
        dayOfWeek: endDay ? expandDayRange(startDay, endDay) : [DAY_CODES[startDay]].filter(Boolean),
        opens,
        closes,
      }
    })
    .filter(Boolean)
}

function normalizeEntityKey(value) {
  const raw = String(value || '').trim()

  if (!raw) {
    return ''
  }

  if (raw === '/') {
    return '/'
  }

  if (raw.startsWith('/')) {
    const path = stripPublicLocalePrefix(raw)
    return path === '/' ? '/' : path.replace(/\/+$/, '')
  }

  return raw.replace(/^\/+|\/+$/g, '')
}

function collectEntityKeys(entity) {
  const keys = new Set()

  const addKey = (value) => {
    const normalized = normalizeEntityKey(value)

    if (!normalized) {
      return
    }

    keys.add(normalized)

    if (normalized !== '/' && !normalized.startsWith('/')) {
      keys.add(`/${normalized}`)
    }
  }

  addKey(entity?.contentKey)
  addKey(entity?.slug)
  addKey(entity?.publicSlug)
  addKey(entity?.path)

  ;(entity?.localized || []).forEach((localized) => {
    addKey(localized?.slug)
    addKey(localized?.publicSlug)
    addKey(localized?.path)
  })

  return keys
}

function matchesEntity(entity, identifier) {
  const normalizedIdentifier = normalizeEntityKey(identifier)

  if (!normalizedIdentifier) {
    return false
  }

  return collectEntityKeys(entity).has(normalizedIdentifier)
}

function findLocalizedMatch(items, locale, identifiers) {
  return items.find((item) => {
    if (normalizePublicLocale(item?.locale) !== locale) {
      return false
    }

    return identifiers.some((identifier) => matchesEntity(item, identifier))
  }) || null
}

function getLocalizedIdentifiers(entity, locale) {
  return (entity?.localized || [])
    .filter((item) => normalizePublicLocale(item?.locale) === locale)
    .flatMap((item) => [item?.slug, item?.publicSlug, item?.path])
    .filter(Boolean)
}

function resolveLocalizedEntity(items = [], identifier, locale = DEFAULT_PUBLIC_LOCALE) {
  const normalizedLocale = normalizePublicLocale(locale)
  const identifiers = [identifier].filter(Boolean)
  const directMatch = findLocalizedMatch(items, normalizedLocale, identifiers)

  if (directMatch) {
    return directMatch
  }

  const baseEntity = items.find((item) => identifiers.some((candidate) => matchesEntity(item, candidate))) || null

  if (!baseEntity) {
    return null
  }

  if (normalizePublicLocale(baseEntity.locale) === normalizedLocale) {
    return baseEntity
  }

  const localizedIdentifiers = getLocalizedIdentifiers(baseEntity, normalizedLocale)
  const localizedMatch = findLocalizedMatch(items, normalizedLocale, localizedIdentifiers)

  if (localizedMatch) {
    return localizedMatch
  }

  return baseEntity
}

function localizedLocationName(location, locale) {
  return location?.localizedContent?.[normalizePublicLocale(locale)]?.name || location?.name
}

async function areaServedSchema(business, locale) {
  const countryItems = (business.areaServed || []).map((name) => ({
    '@type': String(name).toLowerCase().includes('airport') ? 'Airport' : 'Country',
    name,
  }))

  const { siteLocationsSnapshot } = await import('~/generated/site/locations')
  const airportItems = siteLocationsSnapshot
    .filter((location) => location?.isAirport)
    .map((location) => ({
      '@type': 'Airport',
      name: localizedLocationName(location, locale),
    }))

  const seen = new Set()

  return [...airportItems, ...countryItems].filter((item) => {
    const key = `${item['@type']}:${item.name}`

    if (!item.name || seen.has(key)) {
      return false
    }

    seen.add(key)

    return true
  })
}

async function autoRentalSchema(locale) {
  const business = localizedBusiness(locale)
  const address = business.address || {}
  const geo = business.geo || {}
  const latitude = nullableNumber(geo.latitude)
  const longitude = nullableNumber(geo.longitude)
  const hasGeo = latitude !== null && longitude !== null

  return cleanObject({
    '@type': 'AutoRental',
    '@id': businessEntityId(locale),
    url: localeHomeUrl(locale),
    name: business.businessName || siteSettings.siteName || 'Bent.ge',
    brand: {
      '@type': 'Brand',
      name: business.businessName || siteSettings.siteName || 'Bent.ge',
    },
    logo: absoluteUrl(business.logoUrl || '/favicon.ico'),
    image: absoluteUrl(business.logoUrl || '/favicon.ico'),
    email: business.email || siteSettings.supportEmail,
    telephone: business.telephone || siteSettings.supportPhone,
    priceRange: await fleetPriceRange(siteSettings.defaultCurrency),
    address: {
      '@type': 'PostalAddress',
      streetAddress: address.streetAddress,
      addressLocality: address.addressLocality,
      postalCode: address.postalCode,
      addressCountry: address.addressCountry || 'GE',
    },
    geo: hasGeo ? {
      '@type': 'GeoCoordinates',
      latitude,
      longitude,
    } : null,
    openingHoursSpecification: openingHoursSpecification(business.openingHours),
    areaServed: await areaServedSchema(business, locale),
    sameAs: (siteSettings.socialProfiles || []).map((item) => item.href).filter(Boolean),
  })
}

function websiteSchema(locale) {
  return cleanObject({
    '@type': 'WebSite',
    '@id': `${siteOrigin()}/#website`,
    name: siteSettings.siteName || 'Bent.ge',
    url: siteOrigin(),
    publisher: { '@id': businessEntityId(locale) },
    inLanguage: languageCode(siteSettings.defaultLocale),
  })
}

function baseSchema(entity, routePath, type = 'WebPage') {
  const url = entityUrl(entity, routePath)

  return cleanObject({
    '@type': type,
    '@id': `${url}#webpage`,
    url,
    name: entity?.seo?.title || entity?.title || entity?.name,
    description: entity?.seo?.description || entity?.excerpt || entity?.description || entity?.longDescription,
    image: absoluteUrl(entity?.seo?.ogImage || entity?.heroImage || entity?.featuredImage || entity?.image || '', ''),
    inLanguage: languageCode(entity?.locale),
    isPartOf: { '@id': `${siteOrigin()}/#website` },
    publisher: { '@id': businessEntityId(entity?.locale) },
  })
}

function articleSchema(entity, routePath) {
  const url = entityUrl(entity, routePath)

  return cleanObject({
    ...baseSchema(entity, routePath, 'Article'),
    '@id': `${url}#article`,
    headline: entity?.title,
    datePublished: entity?.publishedAt,
    dateModified: entity?.updatedAt || entity?.publishedAt,
    mainEntityOfPage: { '@id': `${url}#webpage` },
    author: { '@id': businessEntityId(entity?.locale) },
  })
}

function vehicleSchema(entity, routePath) {
  const url = entityUrl(entity, routePath)
  const price = Number(entity?.priceFrom || entity?.price || 0)

  return cleanObject({
    '@type': 'Product',
    '@id': `${url}#vehicle`,
    name: entity?.name,
    brand: entity?.brand ? { '@type': 'Brand', name: entity.brand } : null,
    model: entity?.model,
    category: entity?.category,
    description: entity?.seo?.description || entity?.longDescription || entity?.description,
    image: [entity?.heroImage, entity?.image, ...(entity?.gallery || [])].filter(Boolean).map((item) => absoluteUrl(item)),
    url,
    offers: commerceOfferPayload(entity, url, price),
  })
}

function offerSchema(entity, routePath) {
  const url = entityUrl(entity, routePath)
  const price = Number(entity?.currentPrice || entity?.price || 0)
  const relatedVehicle = entity?.relatedVehicle || {}

  return cleanObject({
    '@type': 'Service',
    '@id': `${url}#service`,
    name: entity?.title || entity?.name,
    serviceType: 'Car rental',
    category: relatedVehicle?.type || relatedVehicle?.category || 'Car rental offer',
    description: entity?.seo?.description || entity?.excerpt || entity?.description,
    image: [entity?.heroImage, entity?.featuredImage, entity?.cardImage, entity?.image].filter(Boolean).map((item) => absoluteUrl(item)),
    url,
    provider: { '@id': businessEntityId(entity?.locale) },
    areaServed: {
      '@type': 'Country',
      name: 'Georgia',
    },
    offers: cleanObject({
      ...commerceOfferPayload(entity, url, price),
      '@id': `${url}#offer`,
      seller: { '@id': businessEntityId(entity?.locale) },
    }),
  })
}

function placeSchema(entity, routePath) {
  const url = entityUrl(entity, routePath)

  return cleanObject({
    '@type': 'Place',
    '@id': `${url}#place`,
    name: entity?.name || entity?.cityLabel || entity?.cityName,
    description: entity?.cityDescription || entity?.description,
    image: absoluteUrl(entity?.cityHeroImage || entity?.image || '', ''),
    url,
    address: entity?.address ? {
      '@type': 'PostalAddress',
      streetAddress: entity.address,
      addressLocality: entity.cityName || entity.city,
      addressCountry: 'GE',
    } : null,
  })
}

function faqQuestionsFromPage(entity) {
  const categories = Array.isArray(entity?.structuredContent?.defaultCategories)
    ? entity.structuredContent.defaultCategories
    : []
  const seen = new Set()

  return categories
    .flatMap((category) => Array.isArray(category?.faqs) ? category.faqs : [])
    .map((item) => ({
      question: String(item?.q || item?.question || '').trim(),
      answer: String(item?.a || item?.answer || '').trim(),
    }))
    .filter((item) => {
      const key = `${item.question}:${item.answer}`

      if (!item.question || !item.answer || seen.has(key)) {
        return false
      }

      seen.add(key)

      return true
    })
}

function faqPageSchema(entity, routePath) {
  const questions = faqQuestionsFromPage(entity)

  if (!questions.length) {
    return null
  }

  const url = entityUrl(entity, routePath)

  return cleanObject({
    '@type': 'FAQPage',
    '@id': `${url}#faq`,
    url,
    name: entity?.seo?.title || entity?.title,
    inLanguage: languageCode(entity?.locale),
    mainEntity: questions.map((item) => ({
      '@type': 'Question',
      name: item.question,
      acceptedAnswer: {
        '@type': 'Answer',
        text: item.answer,
      },
    })),
  })
}

async function loadPageItems(locale = DEFAULT_PUBLIC_LOCALE) {
  const normalizedLocale = normalizePublicLocale(locale)
  const loaders = {
    en: () => import('~/generated/site/pages/index/en'),
    ru: () => import('~/generated/site/pages/index/ru'),
    ka: () => import('~/generated/site/pages/index/ka'),
    de: () => import('~/generated/site/pages/index/de'),
    fr: () => import('~/generated/site/pages/index/fr'),
    es: () => import('~/generated/site/pages/index/es'),
    it: () => import('~/generated/site/pages/index/it'),
    pl: () => import('~/generated/site/pages/index/pl'),
    tr: () => import('~/generated/site/pages/index/tr'),
    ar: () => import('~/generated/site/pages/index/ar'),
    he: () => import('~/generated/site/pages/index/he'),
  }
  const { pageItems } = await (loaders[normalizedLocale] || loaders[DEFAULT_PUBLIC_LOCALE])()
  return Array.isArray(pageItems) ? pageItems : []
}

async function loadHomeEntity(locale) {
  return loadHomePage(locale)
}

async function pageEntityByPath(path, locale) {
  const pageItems = await loadPageItems(locale)
  return resolveLocalizedEntity(pageItems, path, locale)
}

function breadcrumbName(entity, fallback = '') {
  return entity?.breadcrumbTitle
    || entity?.title
    || entity?.name
    || entity?.seo?.title
    || fallback
}

async function sectionPageForPath(routePath, locale) {
  const barePath = stripPublicLocalePrefix(routePath || '/')
  const segments = barePath.split('/').filter(Boolean)

  if (segments.length < 2) {
    return null
  }

  const localizedSection = await pageEntityByPath(withPublicLocalePrefix(`/${segments[0]}`, locale), locale)

  if (localizedSection) {
    return localizedSection
  }

  return pageEntityByPath(`/${segments[0]}`, locale)
}

async function breadcrumbSchema(entity, routePath, locale) {
  const normalizedLocale = normalizePublicLocale(locale)
  const barePath = stripPublicLocalePrefix(routePath || '/')

  if (barePath === '/') {
    return null
  }

  const section = await sectionPageForPath(routePath, normalizedLocale)
  const currentUrl = entityUrl(entity, routePath)
  const entries = [
    {
      name: siteSettings.siteName || 'Bent.ge',
      item: localeHomeUrl(normalizedLocale),
    },
    section ? {
      name: breadcrumbName(section),
      item: entityUrl(section, section.path),
    } : null,
    {
      name: breadcrumbName(entity),
      item: currentUrl,
    },
  ]
    .filter((item) => item?.name && item?.item)
    .filter((item, index, items) => index === 0 || item.item !== items[index - 1]?.item)

  if (entries.length < 2) {
    return null
  }

  return {
    '@type': 'BreadcrumbList',
    '@id': `${currentUrl}#breadcrumb`,
    itemListElement: entries.map((item, index) => ({
      '@type': 'ListItem',
      position: index + 1,
      name: item.name,
      item: item.item,
    })),
  }
}

function explicitSchema(entity) {
  return entity?.seo?.schema && typeof entity.seo.schema === 'object'
    ? entity.seo.schema
    : null
}

function entitySchema(type, entity, routePath) {
  const explicit = explicitSchema(entity)

  if (explicit) {
    return explicit
  }

  if (String(entity?.seo?.robots || '').includes('noindex')) {
    return null
  }

  if (type === 'vehicle') {
    return vehicleSchema(entity, routePath)
  }

  if (type === 'offer') {
    return offerSchema(entity, routePath)
  }

  if (type === 'article') {
    return articleSchema(entity, routePath)
  }

  if (type === 'place') {
    return placeSchema(entity, routePath)
  }

  return baseSchema(entity, routePath)
}

async function resolveRouteEntity(path, locale) {
  const routePath = path || '/'
  const barePath = stripPublicLocalePrefix(routePath)

  if (barePath === '/') {
    const entity = await loadHomeEntity(locale)
    return entity ? { type: 'page', entity } : null
  }

  if (barePath.startsWith('/offers/')) {
    const { offers } = await import('~/generated/site/offers')
    const entity = resolveLocalizedEntity(offers, routePath, locale)
    return entity ? { type: 'offer', entity } : null
  }

  if (barePath.startsWith('/news/')) {
    const { newsArticles } = await import('~/generated/site/news/index')
    const entity = resolveLocalizedEntity(newsArticles, routePath, locale)
    return entity ? { type: 'article', entity } : null
  }

  if (barePath.startsWith('/routes/')) {
    const { guideItems } = await import('~/generated/site/guides/index')
    const entity = resolveLocalizedEntity(guideItems, routePath, locale)
    return entity ? { type: 'article', entity } : null
  }

  const vehicleRoute = resolveVehicleRoute({ path: routePath })

  if (vehicleRoute) {
    const entity = await loadResolvedVehicleDetail({ path: routePath }, locale)
    return entity ? { type: 'vehicle', entity } : null
  }

  if (barePath.startsWith('/locations/')) {
    const { siteLocationsSnapshot } = await import('~/generated/site/locations')
    const entity = resolveLocalizedEntity(siteLocationsSnapshot, routePath, locale)
    return entity ? { type: 'place', entity } : null
  }

  const pageEntity = await pageEntityByPath(routePath, locale)
  return pageEntity ? { type: 'page', entity: pageEntity } : null
}

export async function buildPublicJsonLdForRoute(path, locale = DEFAULT_PUBLIC_LOCALE) {
  const routeEntity = await resolveRouteEntity(path || '/', locale)

  if (!routeEntity) {
    return null
  }

  const schema = entitySchema(routeEntity.type, routeEntity.entity, path || '/')

  if (!schema) {
    return null
  }

  const extras = [
    await breadcrumbSchema(routeEntity.entity, path || '/', locale),
    routeEntity.entity?.contentKey === 'faq' ? faqPageSchema(routeEntity.entity, path || '/') : null,
  ].filter(Boolean)

  return cleanObject({
    '@context': 'https://schema.org',
    '@graph': [
      await autoRentalSchema(locale),
      websiteSchema(locale),
      schema,
      ...extras,
    ],
  })
}
