import type { MetadataRoute } from 'next';

import { getCreationList } from '@/services/creations';
import { getCreatorList } from '@/services/creators';
import { getAllNews } from '@/services/news';

import { appLocales, type AppLocale } from '@/config/i18n';
import { CREATOR_PAGES_CONFIG } from '@/config/creator-pages/creatorPagesConfig';
import { metaAttributes110 } from '@/utils/filterUtils';
import { getLanguageAlternates, getLocalizedUrl, type SeoHref } from '@/utils/seo';

type SitemapEntry = MetadataRoute.Sitemap[number];
type SitemapChangeFrequency = NonNullable<SitemapEntry['changeFrequency']>;

type SitemapPage = {
  href: SeoHref;
  locales?: AppLocale[];
  lastModified?: Date | string;
  changeFrequency?: SitemapChangeFrequency;
  priority?: number;
};

type SitemapCreator = {
  alkotoAzonosito?: string | null;
  hasCreations?: boolean;
  hasLifeEvents?: boolean;
  hasMmaNews?: boolean;
  hasNews?: boolean;
  hasCreationVideos?: boolean;
};

type SitemapCreation = {
  alkotasAzonosito?: string | null;
  letrehozasIdeje?: string | null;
  modositasIdeje?: string | null;
};

type SitemapNews = {
  id?: string | number | null;
  megjelenesIdejes?: string | null;
};

const now = () => new Date();

function toEntry(page: SitemapPage, locale: AppLocale): SitemapEntry | null {
  const locales = page.locales ?? appLocales;

  if (!locales.includes(locale)) {
    return null;
  }

  return {
    url: getLocalizedUrl(locale, page.href),
    lastModified: page.lastModified ?? now(),
    changeFrequency: page.changeFrequency,
    priority: page.priority,
    alternates: {
      languages: getLanguageAlternates(page.href, locales),
    },
  };
}

function addPages(target: SitemapEntry[], pages: SitemapPage[], locale: AppLocale) {
  for (const page of pages) {
    const entry = toEntry(page, locale);
    if (entry) {
      target.push(entry);
    }
  }
}

function getDateOrNow(value?: string | null): Date {
  if (!value) return now();

  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? now() : date;
}

function getStaticPages(): SitemapPage[] {
  return [
    {
      href: '/',
      changeFrequency: 'daily',
      priority: 1,
    },
    {
      href: '/alkotok',
      changeFrequency: 'monthly',
      priority: 0.5,
    },
    {
      href: '/alkotok-aktualis-programokkal',
      changeFrequency: 'daily',
      priority: 0.9,
    },
    {
      href: '/parhuzamos-eletutak',
      changeFrequency: 'monthly',
      priority: 0.5,
    },
    {
      href: '/videok',
      changeFrequency: 'weekly',
      priority: 0.7,
    },
    {
      href: '/impresszum',
      changeFrequency: 'yearly',
      priority: 0.2,
    },
    {
      href: '/adatkezelesi-tajekoztato',
      changeFrequency: 'yearly',
      priority: 0.2,
    },
    {
      href: '/sutitajekoztato',
      changeFrequency: 'yearly',
      priority: 0.2,
    },
    {
      href: '/szerzoi-jogok',
      changeFrequency: 'yearly',
      priority: 0.2,
    },
    {
      href: '/projekt-jellemzoi',
      changeFrequency: 'yearly',
      priority: 0.2,
    },
    {
      href: '/oldal-mukodese',
      changeFrequency: 'yearly',
      priority: 0.2,
    },
  ];
}

function getCategoryPages(): SitemapPage[] {
  return metaAttributes110.flatMap((category) => [
    {
      href: `/alkotasok/${category.prettyKey}`,
      changeFrequency: 'weekly' as const,
      priority: 0.5,
    },
    {
      href: `/alkotok/${category.prettyKey}`,
      changeFrequency: 'weekly' as const,
      priority: 0.8,
    },
    {
      href: `/terkep/${category.prettyKey}`,
      changeFrequency: 'monthly' as const,
      priority: 0.5,
    },
    {
      href: `/videok/${category.prettyKey}`,
      changeFrequency: 'weekly' as const,
      priority: 0.6,
    },
  ]);
}

function getCreatorSectionPage(
  id: string,
  section: keyof typeof CREATOR_PAGES_CONFIG,
  priority: number,
  locales?: AppLocale[]
): SitemapPage {
  const config = CREATOR_PAGES_CONFIG[section];

  return {
    href: {
      pathname: config.route,
      params: { id },
    },
    locales: locales ?? config.visibleLocales,
    changeFrequency: 'monthly',
    priority,
  };
}

export async function getSitemapEntries(locale: AppLocale): Promise<MetadataRoute.Sitemap> {
  const isTest = process.env.NEXT_PUBLIC_ENV === 'test';
  if (isTest) {
    return [];
  }

  const entries: MetadataRoute.Sitemap = [];
  addPages(entries, getStaticPages(), locale);
  addPages(entries, getCategoryPages(), locale);

  const apiCalls = [getCreationList(), getCreatorList(), getAllNews()] as const;
  const [creationsResult, creatorsResult, newsResult] = await Promise.allSettled(apiCalls);

  if (creationsResult.status === 'fulfilled') {
    for (const creation of creationsResult.value as SitemapCreation[]) {
      if (!creation.alkotasAzonosito) continue;

      addPages(
        entries,
        [
          {
            href: {
              pathname: '/alkotas/[id]',
              params: { id: creation.alkotasAzonosito },
            },
            lastModified: getDateOrNow(creation.modositasIdeje || creation.letrehozasIdeje),
            changeFrequency: 'monthly',
            priority: 0.6,
          },
        ],
        locale
      );
    }
  } else {
    console.error('Failed to fetch creations for sitemap:', creationsResult.reason);
  }

  if (creatorsResult.status === 'fulfilled') {
    for (const creator of creatorsResult.value as SitemapCreator[]) {
      if (!creator.alkotoAzonosito) continue;

      const id = creator.alkotoAzonosito;
      addPages(
        entries,
        [
          getCreatorSectionPage(id, 'featuredCreations', 0.9),
          ...(creator.hasCreations ? [getCreatorSectionPage(id, 'creations', 0.7)] : []),
          ...(creator.hasLifeEvents ? [getCreatorSectionPage(id, 'lifeEvents', 0.7, ['hu'])] : []),
          ...(creator.hasMmaNews || creator.hasNews
            ? [getCreatorSectionPage(id, 'mmaNews', 0.6, ['hu'])]
            : []),
          ...(creator.hasCreationVideos
            ? [getCreatorSectionPage(id, 'creationVideos', 0.6, ['hu'])]
            : []),
        ],
        locale
      );
    }
  } else {
    console.error('Failed to fetch creators for sitemap:', creatorsResult.reason);
  }

  if (newsResult.status === 'fulfilled') {
    for (const newsItem of newsResult.value as SitemapNews[]) {
      if (!newsItem.id) continue;

      addPages(
        entries,
        [
          {
            href: {
              pathname: '/hir/[id]',
              params: { id: String(newsItem.id) },
            },
            lastModified: getDateOrNow(newsItem.megjelenesIdejes),
            changeFrequency: 'daily',
            priority: 0.4,
          },
        ],
        locale
      );
    }
  } else {
    console.error('Failed to fetch news for sitemap:', newsResult.reason);
  }

  return entries.sort((a, b) => (b.priority || 0) - (a.priority || 0));
}

function escapeXml(value: string): string {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

function formatLastModified(value: SitemapEntry['lastModified']): string | null {
  if (!value) return null;

  return value instanceof Date ? value.toISOString() : value;
}

export function serializeSitemap(entries: MetadataRoute.Sitemap): string {
  const urls = entries
    .map((entry) => {
      const lastModified = formatLastModified(entry.lastModified);
      const changeFrequency = entry.changeFrequency
        ? `<changefreq>${entry.changeFrequency}</changefreq>`
        : '';
      const priority =
        typeof entry.priority === 'number' ? `<priority>${entry.priority.toFixed(1)}</priority>` : '';
      const alternates = Object.entries(entry.alternates?.languages ?? {})
        .filter((entry): entry is [string, string] => typeof entry[1] === 'string')
        .map(([locale, url]) => {
          return `<xhtml:link rel="alternate" hreflang="${escapeXml(locale)}" href="${escapeXml(
            url
          )}" />`;
        })
        .join('');

      return [
        '<url>',
        `<loc>${escapeXml(entry.url)}</loc>`,
        lastModified ? `<lastmod>${escapeXml(lastModified)}</lastmod>` : '',
        changeFrequency,
        priority,
        alternates,
        '</url>',
      ].join('');
    })
    .join('');

  return `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>`;
}
