import { execSync } from "node:child_process";
import path from "node:path";
import type { MetadataRoute } from "next";
import { getPathname } from "@/i18n/navigation";
import { routing } from "@/i18n/routing";

const BASE_URL = "https://www.elevaseo.com";

// Compute lastmod from the last git commit that touched a given file. Runs at
// build time only (SSG).
//
// Vercel clones with `--depth 1` by default and we can't reliably unshallow
// (the `git fetch --unshallow` in `_build` is best-effort but often gets
// silently rejected in the build sandbox). On a shallow clone, `git log` for
// any file older than the shallow boundary returns the SAME boundary-commit
// date — every static page ends up with the same date, which is exactly the
// poisoned signal Google warns against.
//
// Defensive strategy: check `git rev-parse --is-shallow-repository` at module
// load. If shallow, `getGitLastMod` short-circuits to undefined for all static
// pages and the sitemap omits the lastmod field entirely. Google's official
// guidance: no lastmod is preferred over a wrong lastmod.
//
// Blog posts are unaffected — they use `post.lastmod` from MDX frontmatter,
// which doesn't depend on git history.
const hasFullGitHistory = (() => {
  try {
    const result = execSync("git rev-parse --is-shallow-repository", {
      cwd: path.resolve(process.cwd()),
      stdio: ["ignore", "pipe", "ignore"],
    });
    return result.toString().trim() === "false";
  } catch {
    return false;
  }
})();

function getGitLastMod(relPath: string): Date | undefined {
  if (!hasFullGitHistory) {
    return;
  }
  try {
    // --no-merges skips merge commits so a `dev → main` merge doesn't stamp
    // every file with the merge-commit date; git walks down to the actual
    // content-change commit instead.
    const stdout = execSync(
      `git log -1 --no-merges --format=%cI -- ${relPath}`,
      {
        cwd: path.resolve(process.cwd()),
        stdio: ["ignore", "pipe", "ignore"],
      }
    );
    const dateStr = stdout.toString().trim();
    if (!dateStr) {
      return;
    }
    return new Date(dateStr);
  } catch {
    return;
  }
}

interface StaticPage {
  href: string;
  /**
   * Source file used to derive lastmod via git log. Relative to repo root.
   * For pages under app/[locale]/, the actual page.tsx lives at that path.
   */
  source: string;
}

const staticPages: StaticPage[] = [
  { href: "/", source: "app/[locale]/page.tsx" },
  { href: "/blog", source: "app/[locale]/blog/page.tsx" },
  { href: "/contact", source: "app/[locale]/contact/page.tsx" },
  { href: "/services", source: "app/[locale]/services/page.tsx" },
  {
    href: "/services/audits",
    source: "app/[locale]/services/audits/page.tsx",
  },
  {
    href: "/services/audits/performance",
    source: "app/[locale]/services/audits/performance/page.tsx",
  },
  {
    href: "/services/builds",
    source: "app/[locale]/services/builds/page.tsx",
  },
  {
    href: "/services/builds/shopify-headless",
    source: "app/[locale]/services/builds/shopify-headless/page.tsx",
  },
  {
    href: "/services/builds/wordpress-headless",
    source: "app/[locale]/services/builds/wordpress-headless/page.tsx",
  },
  { href: "/services/cro", source: "app/[locale]/services/cro/page.tsx" },
  {
    href: "/services/training",
    source: "app/[locale]/services/training/page.tsx",
  },
  {
    href: "/services/training/ai-for-marketers",
    source: "app/[locale]/services/training/ai-for-marketers/page.tsx",
  },
  {
    href: "/services/training/seo-technical",
    source: "app/[locale]/services/training/seo-technical/page.tsx",
  },
  {
    href: "/services/wordpress",
    source: "app/[locale]/services/wordpress/page.tsx",
  },
  {
    href: "/services/wordpress/maintenance",
    source: "app/[locale]/services/wordpress/maintenance/page.tsx",
  },
  {
    href: "/services/wordpress/malware-removal",
    source: "app/[locale]/services/wordpress/malware-removal/page.tsx",
  },
  {
    href: "/services/wordpress/security",
    source: "app/[locale]/services/wordpress/security/page.tsx",
  },
  {
    href: "/audits/cro-audit",
    source: "app/[locale]/audits/cro-audit/page.tsx",
  },
  {
    href: "/audits/headless-data-analysis",
    source: "app/[locale]/audits/headless-data-analysis/page.tsx",
  },
  {
    href: "/audits/headless-performance-audit",
    source: "app/[locale]/audits/headless-performance-audit/page.tsx",
  },
  {
    href: "/audits/shopify-speed-audit",
    source: "app/[locale]/audits/shopify-speed-audit/page.tsx",
  },
  { href: "/about", source: "app/[locale]/about/page.tsx" },
  {
    href: "/privacy-policy",
    source: "app/[locale]/privacy-policy/page.tsx",
  },
  { href: "/terms", source: "app/[locale]/terms/page.tsx" },
];

function getUrl(href: string, locale: string) {
  const pathname = getPathname({ locale, href });
  return `${BASE_URL}${pathname}`;
}

function buildLocaleAlternates(href: string): Record<string, string> {
  const alternates: Record<string, string> = {};
  for (const locale of routing.locales) {
    alternates[locale] = getUrl(href, locale);
  }
  alternates["x-default"] = alternates[routing.defaultLocale];
  return alternates;
}

function buildStaticEntries(): MetadataRoute.Sitemap {
  return staticPages.flatMap((page) => {
    const lastModified = getGitLastMod(page.source);
    const alternates = buildLocaleAlternates(page.href);

    return routing.locales.map((locale) => ({
      url: getUrl(page.href, locale),
      // Conditionally include lastModified — omit entirely when git history
      // is unavailable rather than emit a misleading build-time date.
      ...(lastModified ? { lastModified } : {}),
      alternates: { languages: alternates },
    }));
  });
}

interface PostInfo {
  categorySlug: string;
  lastmod: string;
  slug: string;
}

interface TranslationGroup {
  en?: PostInfo;
  fr?: PostInfo;
}

interface PublishedPost {
  categorySlug: string;
  lastmod: string;
  locale: string;
  published: boolean;
  slug: string;
  translationKey: string;
}

function groupPostsByTranslation(
  publishedPosts: PublishedPost[]
): Map<string, TranslationGroup> {
  const translationGroups = new Map<string, TranslationGroup>();
  for (const post of publishedPosts) {
    const group = translationGroups.get(post.translationKey) || {};
    group[post.locale as "fr" | "en"] = {
      slug: post.slug,
      categorySlug: post.categorySlug,
      lastmod: post.lastmod,
    };
    translationGroups.set(post.translationKey, group);
  }
  return translationGroups;
}

function buildBlogPostEntries(
  translationGroups: Map<string, TranslationGroup>
): MetadataRoute.Sitemap {
  const entries: MetadataRoute.Sitemap = [];
  for (const [, group] of translationGroups) {
    const alternates: Record<string, string> = {};
    if (group.fr) {
      alternates.fr = getUrl(
        `/blog/${group.fr.categorySlug}/${group.fr.slug}`,
        "fr"
      );
    }
    if (group.en) {
      alternates.en = getUrl(
        `/blog/${group.en.categorySlug}/${group.en.slug}`,
        "en"
      );
    }
    if (group.fr) {
      alternates["x-default"] = alternates.fr;
    } else if (group.en) {
      alternates["x-default"] = alternates.en;
    }

    if (group.fr) {
      entries.push({
        url: alternates.fr,
        lastModified: new Date(group.fr.lastmod),
        alternates: { languages: alternates },
      });
    }
    if (group.en) {
      entries.push({
        url: alternates.en,
        lastModified: new Date(group.en.lastmod),
        alternates: { languages: alternates },
      });
    }
  }
  return entries;
}

function buildCategoryLastModMap(
  publishedPosts: PublishedPost[]
): Map<string, Date> {
  const categoryLastMod = new Map<string, Date>();
  for (const post of publishedPosts) {
    const current = categoryLastMod.get(post.categorySlug);
    const postDate = new Date(post.lastmod);
    if (!current || postDate > current) {
      categoryLastMod.set(post.categorySlug, postDate);
    }
  }
  return categoryLastMod;
}

function buildCategoryEntries(
  categoryLastMod: Map<string, Date>
): MetadataRoute.Sitemap {
  const entries: MetadataRoute.Sitemap = [];
  for (const [catSlug, lastModified] of categoryLastMod) {
    const href = `/blog/${catSlug}`;
    const alternates = buildLocaleAlternates(href);

    for (const locale of routing.locales) {
      entries.push({
        url: getUrl(href, locale),
        lastModified,
        alternates: { languages: alternates },
      });
    }
  }
  return entries;
}

async function buildBlogEntries(): Promise<MetadataRoute.Sitemap> {
  try {
    const { allBlogPosts } = await import("content-collections");
    const publishedPosts = (allBlogPosts as PublishedPost[]).filter(
      (post) => post.published
    );

    const translationGroups = groupPostsByTranslation(publishedPosts);
    const categoryLastMod = buildCategoryLastModMap(publishedPosts);

    return [
      ...buildBlogPostEntries(translationGroups),
      ...buildCategoryEntries(categoryLastMod),
    ];
  } catch (error) {
    if (
      error instanceof Error &&
      error.message.includes("Cannot find module")
    ) {
      console.warn(
        "Content collections not generated yet, skipping blog sitemap entries."
      );
    } else {
      console.error("Error generating blog sitemap entries:", error);
    }
    return [];
  }
}

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const staticEntries = buildStaticEntries();
  const blogEntries = await buildBlogEntries();
  return [...staticEntries, ...blogEntries];
}
