import { NextResponse } from "next/server";
import { DepartmentKey, MarketingSyncRunStatus } from "@prisma/client";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { buildInsightsFromSnapshots, rollupSnapshotsFromDailyMetrics } from "@/lib/marketing/meta";
import { canManageMarketing } from "@/lib/marketing/persistence";
import { publishMarketingKpis } from "@/lib/kpis/publishers";
import { getWeekBounds } from "@/lib/kpis/date";
import {
  completeCaptureAutomationRunFailure,
  completeCaptureAutomationRunSuccess,
  startCaptureAutomationRun,
} from "@/lib/capture";

type SyncMetricInput = {
  channel: "facebook" | "instagram";
  date: string;
  followers: number;
  reach: number;
  impressions: number;
  engagements: number;
};

type SyncRequestBody = {
  metrics?: SyncMetricInput[];
  publishWeekStart?: string;
};

function hasValidCronSecret(request: Request): boolean {
  const configuredSecret = process.env.MARKETING_SYNC_CRON_SECRET?.trim();
  if (!configuredSecret) {
    return false;
  }

  const authorization = request.headers.get("authorization");
  if (!authorization) {
    return false;
  }

  const [scheme, rawToken] = authorization.split(" ");
  if (scheme?.toLowerCase() !== "bearer") {
    return false;
  }

  return rawToken?.trim() === configuredSecret;
}

function canUseDirectInstagramMode(): boolean {
  const token = process.env.INSTAGRAM_TOKEN?.trim();
  const userId = process.env.INSTAGRAM_USER_ID?.trim();
  return Boolean(token && userId);
}

async function resolveSyncActorUserId(input: {
  sessionUserId?: string;
  connectedById?: string | null;
}): Promise<string> {
  if (input.sessionUserId) {
    return input.sessionUserId;
  }

  if (input.connectedById) {
    return input.connectedById;
  }

  const owner = await prisma.user.findFirst({
    where: {
      status: "active",
      userRoles: {
        some: {
          role: {
            key: "owner",
          },
        },
      },
    },
    select: {
      id: true,
    },
    orderBy: [{ createdAt: "asc" }],
  });

  if (!owner?.id) {
    throw new Error("No owner user available for scheduled marketing sync.");
  }

  return owner.id;
}

function todayIso(): string {
  return new Date().toISOString().slice(0, 10);
}

function sanitizeMetric(input: SyncMetricInput) {
  const date = /^\d{4}-\d{2}-\d{2}$/.test(input.date) ? input.date : todayIso();
  const followers = Math.max(0, Math.round(input.followers));
  const reach = Math.max(0, Math.round(input.reach));
  const impressions = Math.max(0, Math.round(input.impressions));
  const engagements = Math.max(0, Math.round(input.engagements));
  const engagementRate = impressions > 0 ? (engagements / impressions) * 100 : 0;

  return {
    channel: input.channel,
    metricDate: date,
    followers,
    reach,
    impressions,
    engagements,
    engagementRate,
  };
}

async function fetchFacebookMetrics(connection: { pageId: string; pageAccessToken: string }) {
  if (connection.pageId.startsWith("ig-direct-")) {
    return [] as ReturnType<typeof sanitizeMetric>[];
  }

  const until = new Date();
  const since = new Date(until.getTime() - 31 * 24 * 60 * 60 * 1000);
  const endpoint = new URL(`https://graph.facebook.com/v22.0/${connection.pageId}/insights`);
  endpoint.searchParams.set("metric", "page_fans,page_impressions,page_post_engagements");
  endpoint.searchParams.set("period", "day");
  endpoint.searchParams.set("since", since.toISOString().slice(0, 10));
  endpoint.searchParams.set("until", until.toISOString().slice(0, 10));
  endpoint.searchParams.set("access_token", connection.pageAccessToken);

  const response = await fetch(endpoint, { method: "GET", cache: "no-store" });
  if (!response.ok) {
    const text = await response.text();
    throw new Error(`Meta sync failed (${response.status}): ${text.slice(0, 180)}`);
  }

  const payload = (await response.json()) as {
    data?: Array<{ name?: string; values?: Array<{ end_time?: string; value?: number | { value?: number } }> }>;
  };

  const byDate = new Map<string, { followers?: number; impressions?: number; engagements?: number }>();
  for (const metric of payload.data ?? []) {
    for (const point of metric.values ?? []) {
      const date = point.end_time?.slice(0, 10);
      if (!date) {
        continue;
      }

      const existing = byDate.get(date) ?? {};
      const rawValue = typeof point.value === "number" ? point.value : point.value?.value ?? 0;
      if (metric.name === "page_fans") {
        existing.followers = Number(rawValue);
      } else if (metric.name === "page_impressions") {
        existing.impressions = Number(rawValue);
      } else if (metric.name === "page_post_engagements") {
        existing.engagements = Number(rawValue);
      }
      byDate.set(date, existing);
    }
  }

  const rows = Array.from(byDate.entries())
    .sort((a, b) => a[0].localeCompare(b[0]))
    .map(([date, value]) => {
      const impressions = Math.max(0, Math.round(value.impressions ?? 0));
      const engagements = Math.max(0, Math.round(value.engagements ?? 0));
      return {
        channel: "facebook" as const,
        metricDate: date,
        followers: Math.max(0, Math.round(value.followers ?? 0)),
        reach: impressions,
        impressions,
        engagements,
        engagementRate: impressions > 0 ? (engagements / impressions) * 100 : 0,
      };
    });

  return rows;
}

type InstagramApiMode = "instagram" | "facebook";
type InstagramDailyInsight = {
  rawFollowers?: number;
  reach?: number;
  views?: number;
  totalInteractions?: number;
  accountsEngaged?: number;
};

type InstagramMediaDailyAggregate = {
  views: number;
  interactions: number;
  likes: number;
  comments: number;
  shares: number;
  saves: number;
};

function parseMetricValue(value: number | { value?: number } | undefined): number {
  if (typeof value === "number") {
    return Number(value);
  }
  return Number(value?.value ?? 0);
}

function baseApiUrl(mode: InstagramApiMode): string {
  return mode === "instagram" ? "https://graph.instagram.com/v25.0" : "https://graph.facebook.com/v22.0";
}

async function fetchInstagramProfileFollowers(input: {
  mode: InstagramApiMode;
  resourceId: string;
  accessToken: string;
}): Promise<number> {
  const endpoint = new URL(`${baseApiUrl(input.mode)}/${input.resourceId}`);
  endpoint.searchParams.set("fields", "followers_count");
  endpoint.searchParams.set("access_token", input.accessToken);

  const response = await fetch(endpoint, { method: "GET", cache: "no-store" });
  if (!response.ok) {
    return 0;
  }

  const payload = (await response.json()) as { followers_count?: number };
  return Math.max(0, Math.round(payload.followers_count ?? 0));
}

async function fetchInstagramAccountDailyInsights(input: {
  mode: InstagramApiMode;
  resourceId: string;
  accessToken: string;
  since: Date;
  until: Date;
}): Promise<Map<string, InstagramDailyInsight>> {
  const endpoint = new URL(`${baseApiUrl(input.mode)}/${input.resourceId}/insights`);
  endpoint.searchParams.set("metric", "reach,follower_count,total_interactions,accounts_engaged,views");
  endpoint.searchParams.set("period", "day");
  endpoint.searchParams.set("since", input.since.toISOString().slice(0, 10));
  endpoint.searchParams.set("until", input.until.toISOString().slice(0, 10));
  endpoint.searchParams.set("access_token", input.accessToken);

  const response = await fetch(endpoint, { method: "GET", cache: "no-store" });
  if (!response.ok) {
    const text = await response.text();
    throw new Error(`Instagram account insights failed (${response.status}): ${text.slice(0, 180)}`);
  }

  const payload = (await response.json()) as {
    data?: Array<{ name?: string; values?: Array<{ end_time?: string; value?: number | { value?: number } }> }>;
  };

  const byDate = new Map<string, InstagramDailyInsight>();
  for (const metric of payload.data ?? []) {
    for (const point of metric.values ?? []) {
      const date = point.end_time?.slice(0, 10);
      if (!date) {
        continue;
      }

      const entry = byDate.get(date) ?? {};
      const rawValue = parseMetricValue(point.value);
      if (metric.name === "reach") {
        entry.reach = rawValue;
      } else if (metric.name === "follower_count") {
        entry.rawFollowers = rawValue;
      } else if (metric.name === "views") {
        entry.views = rawValue;
      } else if (metric.name === "total_interactions") {
        entry.totalInteractions = rawValue;
      } else if (metric.name === "accounts_engaged") {
        entry.accountsEngaged = rawValue;
      }
      byDate.set(date, entry);
    }
  }

  return byDate;
}

async function fetchInstagramMediaInsightsSummary(input: {
  mode: InstagramApiMode;
  mediaId: string;
  accessToken: string;
}): Promise<{
  views: number;
  totalInteractions: number;
  likes: number;
  comments: number;
  shares: number;
  saves: number;
} | null> {
  const metricCandidates = [
    "reach,likes,comments,shares,saved,total_interactions,views",
    "reach,likes,comments,shares,saved,total_interactions",
    "reach,likes,comments,saved,total_interactions",
    "reach,likes,comments,views",
    "reach,likes,comments",
  ];

  for (const metricList of metricCandidates) {
    const endpoint = new URL(`${baseApiUrl(input.mode)}/${input.mediaId}/insights`);
    endpoint.searchParams.set("metric", metricList);
    endpoint.searchParams.set("access_token", input.accessToken);

    const response = await fetch(endpoint, { method: "GET", cache: "no-store" });
    if (!response.ok) {
      const text = await response.text();
      const unsupportedMetric = text.includes("does not support") || text.includes("must be one of the following values");
      if (unsupportedMetric) {
        continue;
      }
      return null;
    }

    const payload = (await response.json()) as {
      data?: Array<{ name?: string; values?: Array<{ value?: number | { value?: number } }> }>;
    };

    let views = 0;
    let totalInteractions = 0;
    let likes = 0;
    let comments = 0;
    let shares = 0;
    let saves = 0;

    for (const metric of payload.data ?? []) {
      const rawValue = parseMetricValue(metric.values?.[0]?.value);
      if (metric.name === "views") {
        views = rawValue;
      } else if (metric.name === "total_interactions") {
        totalInteractions = rawValue;
      } else if (metric.name === "likes") {
        likes = rawValue;
      } else if (metric.name === "comments") {
        comments = rawValue;
      } else if (metric.name === "shares") {
        shares = rawValue;
      } else if (metric.name === "saved") {
        saves = rawValue;
      }
    }

    return {
      views: Math.max(0, Math.round(views)),
      totalInteractions: Math.max(0, Math.round(totalInteractions)),
      likes: Math.max(0, Math.round(likes)),
      comments: Math.max(0, Math.round(comments)),
      shares: Math.max(0, Math.round(shares)),
      saves: Math.max(0, Math.round(saves)),
    };
  }

  return null;
}

async function fetchInstagramMediaDailyRollup(input: {
  mode: InstagramApiMode;
  userId: string;
  accessToken: string;
  since: Date;
  until: Date;
}): Promise<Map<string, InstagramMediaDailyAggregate>> {
  const sinceIso = input.since.toISOString().slice(0, 10);
  const untilIso = input.until.toISOString().slice(0, 10);
  const mediaItems: Array<{
    id: string;
    timestamp: string;
    like_count?: number;
    comments_count?: number;
  }> = [];

  let nextUrl: string | null = (() => {
    const endpoint = new URL(`${baseApiUrl(input.mode)}/${input.userId}/media`);
    endpoint.searchParams.set("fields", "id,timestamp,like_count,comments_count,media_type");
    endpoint.searchParams.set("limit", "25");
    endpoint.searchParams.set("access_token", input.accessToken);
    return endpoint.toString();
  })();

  for (let page = 0; page < 6 && nextUrl && mediaItems.length < 30; page += 1) {
    const response = await fetch(nextUrl, { method: "GET", cache: "no-store" });
    if (!response.ok) {
      break;
    }

    const payload = (await response.json()) as {
      data?: Array<{ id?: string; timestamp?: string; like_count?: number; comments_count?: number }>;
      paging?: { next?: string };
    };

    let sawRecentMedia = false;
    for (const media of payload.data ?? []) {
      if (!media.id || !media.timestamp) {
        continue;
      }

      const date = media.timestamp.slice(0, 10);
      if (date < sinceIso) {
        continue;
      }

      if (date > untilIso) {
        continue;
      }

      sawRecentMedia = true;
      mediaItems.push({
        id: media.id,
        timestamp: media.timestamp,
        like_count: media.like_count,
        comments_count: media.comments_count,
      });

      if (mediaItems.length >= 30) {
        break;
      }
    }

    if (!sawRecentMedia) {
      const oldestDate = payload.data?.[payload.data.length - 1]?.timestamp?.slice(0, 10);
      if (oldestDate && oldestDate < sinceIso) {
        break;
      }
    }

    nextUrl = payload.paging?.next ?? null;
  }

  const daily = new Map<string, InstagramMediaDailyAggregate>();
  for (const media of mediaItems) {
    const date = media.timestamp.slice(0, 10);
    const likes = Math.max(0, Math.round(media.like_count ?? 0));
    const comments = Math.max(0, Math.round(media.comments_count ?? 0));
    const insight = await fetchInstagramMediaInsightsSummary({
      mode: input.mode,
      mediaId: media.id,
      accessToken: input.accessToken,
    });

    const shares = insight?.shares ?? 0;
    const saves = insight?.saves ?? 0;
    const totalInteractions = insight?.totalInteractions ?? likes + comments + shares + saves;
    const views = insight?.views ?? 0;
    const existing = daily.get(date) ?? {
      views: 0,
      interactions: 0,
      likes: 0,
      comments: 0,
      shares: 0,
      saves: 0,
    };

    existing.views += views;
    existing.interactions += totalInteractions;
    existing.likes += likes;
    existing.comments += comments;
    existing.shares += shares;
    existing.saves += saves;
    daily.set(date, existing);
  }

  return daily;
}

async function fetchInstagramMetrics(connection: { pageId: string; pageAccessToken: string }) {
  const configuredInstagramToken = process.env.INSTAGRAM_TOKEN?.trim();
  const configuredInstagramUserId = process.env.INSTAGRAM_USER_ID?.trim();

  let mode: InstagramApiMode;
  let userId: string;
  let accessToken: string;

  if (configuredInstagramToken && configuredInstagramUserId) {
    mode = "instagram";
    userId = configuredInstagramUserId;
    accessToken = configuredInstagramToken;
  } else {
    const pageEndpoint = new URL(`https://graph.facebook.com/v22.0/${connection.pageId}`);
    pageEndpoint.searchParams.set("fields", "instagram_business_account{id}");
    pageEndpoint.searchParams.set("access_token", connection.pageAccessToken);

    const pageResponse = await fetch(pageEndpoint, { method: "GET", cache: "no-store" });
    if (!pageResponse.ok) {
      const text = await pageResponse.text();
      throw new Error(`Meta IG discovery failed (${pageResponse.status}): ${text.slice(0, 180)}`);
    }

    const pagePayload = (await pageResponse.json()) as {
      instagram_business_account?: { id?: string };
    };
    const igAccountId = pagePayload.instagram_business_account?.id;
    if (!igAccountId) {
      return [] as ReturnType<typeof sanitizeMetric>[];
    }

    mode = "facebook";
    userId = igAccountId;
    accessToken = connection.pageAccessToken;
  }

  const until = new Date();
  const since = new Date(until.getTime() - 31 * 24 * 60 * 60 * 1000);
  const [profileFollowers, accountDaily, mediaDaily] = await Promise.all([
    fetchInstagramProfileFollowers({
      mode,
      resourceId: userId,
      accessToken,
    }),
    fetchInstagramAccountDailyInsights({
      mode,
      resourceId: userId,
      accessToken,
      since,
      until,
    }),
    fetchInstagramMediaDailyRollup({
      mode,
      userId,
      accessToken,
      since,
      until,
    }),
  ]);

  const dates = new Set<string>([
    ...Array.from(accountDaily.keys()),
    ...Array.from(mediaDaily.keys()),
  ]);

  let previousFollowers = profileFollowers;
  const rows = Array.from(dates.values())
    .sort((a, b) => a.localeCompare(b))
    .map((date) => {
      const account = accountDaily.get(date);
      const media = mediaDaily.get(date);
      const reach = Math.max(0, Math.round(account?.reach ?? 0));
      const rawFollowers = Math.max(0, Math.round(account?.rawFollowers ?? 0));
      const followers =
        profileFollowers > 0 ? profileFollowers : rawFollowers > 0 ? rawFollowers : previousFollowers;

      previousFollowers = followers;

      const accountViews = Math.max(0, Math.round(account?.views ?? 0));
      const mediaViews = Math.max(0, Math.round(media?.views ?? 0));
      const impressions = accountViews > 0 ? accountViews : mediaViews > 0 ? mediaViews : reach;

      const accountInteractions = Math.max(
        0,
        Math.round((account?.totalInteractions ?? 0) > 0 ? (account?.totalInteractions ?? 0) : (account?.accountsEngaged ?? 0))
      );
      const mediaInteractions = Math.max(0, Math.round(media?.interactions ?? 0));
      const engagements = accountInteractions > 0 ? accountInteractions : mediaInteractions;

      return {
        channel: "instagram" as const,
        metricDate: date,
        followers,
        reach,
        impressions,
        engagements,
        engagementRate: impressions > 0 ? (engagements / impressions) * 100 : 0,
      };
    })
    .filter((row) => row.reach > 0 || row.impressions > 0 || row.engagements > 0 || row.followers > 0);

  return rows;
}

async function fallbackFromSnapshots() {
  const snapshots = await prisma.marketingSocialSnapshot.findMany({
    where: { department: DepartmentKey.marketing, range: "30d" },
    orderBy: { capturedAt: "desc" },
    take: 2,
  });

  if (snapshots.length === 0) {
    return [] as ReturnType<typeof sanitizeMetric>[];
  }

  const rows: ReturnType<typeof sanitizeMetric>[] = [];
  const reference = new Date();
  for (const snapshot of snapshots) {
    for (let day = 29; day >= 0; day -= 1) {
      const ratio = (29 - day) / 29;
      const followers = Math.round(snapshot.followersStart + (snapshot.followersEnd - snapshot.followersStart) * ratio);
      const date = new Date(reference.getTime() - day * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
      rows.push({
        channel: snapshot.channel === "instagram" ? "instagram" : "facebook",
        metricDate: date,
        followers,
        reach: Math.max(0, Math.round(snapshot.reach / 30)),
        impressions: Math.max(0, Math.round(snapshot.impressions / 30)),
        engagements: Math.max(0, Math.round((snapshot.impressions * (snapshot.engagementRate / 100)) / 30)),
        engagementRate: snapshot.engagementRate,
      });
    }
  }

  return rows;
}

export async function POST(request: Request) {
  const session = await getServerSession(authOptions);
  const isCronRequest = hasValidCronSecret(request);
  if (!session?.user && !isCronRequest) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (!isCronRequest && session?.user && !canManageMarketing(session.user.role, session.user.department)) {
    return NextResponse.json({ error: "Only owner/marketing leader can trigger Meta sync." }, { status: 403 });
  }

  let connection = await prisma.marketingMetaConnection.findFirst({
    where: {
      department: DepartmentKey.marketing,
      isActive: true,
    },
    orderBy: { updatedAt: "desc" },
  });

  if (!connection && canUseDirectInstagramMode()) {
    const instagramUserId = process.env.INSTAGRAM_USER_ID!.trim();
    const instagramToken = process.env.INSTAGRAM_TOKEN!.trim();
    connection = await prisma.marketingMetaConnection.create({
      data: {
        department: DepartmentKey.marketing,
        accountId: "instagram-direct",
        pageId: `ig-direct-${instagramUserId}`,
        pageName: "Instagram Direct",
        pageAccessToken: instagramToken,
        connectedById: session?.user?.id ?? null,
        isActive: true,
      },
    });
  }

  if (!connection) {
    return NextResponse.json(
      { error: "Meta connection is not configured. Set one in UI or define INSTAGRAM_TOKEN + INSTAGRAM_USER_ID." },
      { status: 400 }
    );
  }

  const actorUserId = await resolveSyncActorUserId({
    sessionUserId: session?.user?.id,
    connectedById: connection.connectedById,
  });

  const body = (await request.json().catch(() => ({}))) as SyncRequestBody;
  const { weekStart: captureWeekStart } = getWeekBounds(body.publishWeekStart);
  const captureRun = await startCaptureAutomationRun({
    department: DepartmentKey.marketing,
    weekStart: captureWeekStart,
    source: isCronRequest ? "marketing-meta-sync-cron-v1" : "marketing-meta-sync-v1",
    triggeredById: actorUserId,
  });
  const syncRun = await prisma.marketingSyncRun.create({
    data: {
      department: DepartmentKey.marketing,
      connectionId: connection.id,
      status: MarketingSyncRunStatus.running,
      triggeredById: actorUserId,
      message: isCronRequest ? "Scheduled sync started (06:00)." : "Sync started.",
    },
  });

  try {
    let metrics = (body.metrics ?? []).map(sanitizeMetric);
    let syncMessage = "Synced from provided payload.";

    if (metrics.length === 0) {
      try {
        const [facebookResult, instagramResult] = await Promise.allSettled([
          fetchFacebookMetrics(connection),
          fetchInstagramMetrics(connection),
        ]);
        const facebookMetrics = facebookResult.status === "fulfilled" ? facebookResult.value : [];
        const instagramMetrics = instagramResult.status === "fulfilled" ? instagramResult.value : [];
        metrics = [...facebookMetrics, ...instagramMetrics];
        if (metrics.length === 0) {
          if (facebookResult.status === "rejected") {
            throw facebookResult.reason;
          }
          if (instagramResult.status === "rejected") {
            throw instagramResult.reason;
          }
          throw new Error("Meta Graph API did not return any rows.");
        }
        syncMessage =
          instagramMetrics.length > 0
            ? "Synced from Meta Graph API (Facebook + Instagram)."
            : "Synced from Meta Graph API (Facebook).";
      } catch (error) {
        const fallback = await fallbackFromSnapshots();
        if (fallback.length === 0) {
          throw error;
        }
        metrics = fallback;
        syncMessage = "Graph API unavailable; fallback generated from historical snapshots.";
      }
    }

    for (const metric of metrics) {
      await prisma.marketingSocialMetricDaily.upsert({
        where: {
          connectionId_channel_metricDate: {
            connectionId: connection.id,
            channel: metric.channel,
            metricDate: new Date(`${metric.metricDate}T00:00:00.000Z`),
          },
        },
        create: {
          department: DepartmentKey.marketing,
          connectionId: connection.id,
          channel: metric.channel,
          metricDate: new Date(`${metric.metricDate}T00:00:00.000Z`),
          followers: metric.followers,
          reach: metric.reach,
          impressions: metric.impressions,
          engagements: metric.engagements,
          engagementRate: metric.engagementRate,
        },
        update: {
          followers: metric.followers,
          reach: metric.reach,
          impressions: metric.impressions,
          engagements: metric.engagements,
          engagementRate: metric.engagementRate,
        },
      });
    }

    const dailyMetrics = await prisma.marketingSocialMetricDaily.findMany({
      where: {
        connectionId: connection.id,
        metricDate: {
          gte: new Date(new Date().getTime() - 45 * 24 * 60 * 60 * 1000),
        },
      },
      orderBy: { metricDate: "asc" },
    });

    const rolledSnapshots = rollupSnapshotsFromDailyMetrics(
      dailyMetrics.map((row) => ({
        channel: row.channel === "instagram" ? "instagram" : "facebook",
        metricDate: row.metricDate.toISOString().slice(0, 10),
        followers: row.followers,
        reach: row.reach,
        impressions: row.impressions,
        engagements: row.engagements,
        engagementRate: row.engagementRate,
      }))
    );

    for (const snapshot of rolledSnapshots) {
      const capturedAt = new Date(`${snapshot.capturedAt}T00:00:00.000Z`);
      await prisma.marketingSocialSnapshot.deleteMany({
        where: {
          department: DepartmentKey.marketing,
          channel: snapshot.channel,
          range: snapshot.range,
          capturedAt,
        },
      });
      await prisma.marketingSocialSnapshot.create({
        data: {
          department: DepartmentKey.marketing,
          channel: snapshot.channel,
          range: snapshot.range,
          followersStart: snapshot.followersStart,
          followersEnd: snapshot.followersEnd,
          engagementRate: snapshot.engagementRate,
          reach: snapshot.reach,
          impressions: snapshot.impressions,
          capturedAt,
        },
      });
    }

    await prisma.marketingMetaConnection.update({
      where: { id: connection.id },
      data: { lastSyncedAt: new Date() },
    });

    await prisma.marketingSyncRun.update({
      where: { id: syncRun.id },
      data: {
        status: MarketingSyncRunStatus.success,
        rowsIngested: metrics.length,
        message: syncMessage,
        completedAt: new Date(),
      },
    });

    const socialInsights = buildInsightsFromSnapshots(
      rolledSnapshots.map((item, index) => ({
        id: `${index}`,
        channel: item.channel,
        range: item.range,
        followersStart: item.followersStart,
        followersEnd: item.followersEnd,
        engagementRate: item.engagementRate,
        reach: item.reach,
        impressions: item.impressions,
        capturedAt: new Date(`${item.capturedAt}T00:00:00.000Z`).toISOString(),
      }))
    );

    const insights7d = socialInsights.filter((item) => item.range === "7d");
    const growthPct7d =
      insights7d.length > 0 ? insights7d.reduce((acc, item) => acc + item.followerGrowthPct, 0) / insights7d.length : 0;
    const engagementPct7d =
      insights7d.length > 0 ? insights7d.reduce((acc, item) => acc + item.engagementRatePct, 0) / insights7d.length : 0;
    const instagram7d = insights7d.find((item) => item.channel === "instagram");
    const instagramReach7d = instagram7d?.reach ?? 0;
    const instagramFollowers = instagram7d?.followersEnd ?? 0;
    const activeInitiatives = await prisma.marketingInitiative.count({
      where: {
        department: DepartmentKey.marketing,
        status: {
          in: ["planning", "in_progress", "completion"],
        },
      },
    });

    const initiativesWithAttribution = await prisma.marketingInitiative.findMany({
      where: {
        department: DepartmentKey.marketing,
        attributionChannel: {
          not: null,
        },
      },
      select: {
        id: true,
        attributionChannel: true,
        attributionPageId: true,
        attributionCampaign: true,
        attributionStart: true,
        attributionEnd: true,
      },
    });

    for (const initiative of initiativesWithAttribution) {
      if (initiative.attributionPageId && initiative.attributionPageId !== connection.pageId) {
        continue;
      }
      if (initiative.attributionCampaign && initiative.attributionCampaign.trim()) {
        // Campaign-level mapping is deferred until campaign-tagged metrics are ingested.
        continue;
      }

      const channel = initiative.attributionChannel === "instagram" ? "instagram" : "facebook";
      const windowStart =
        initiative.attributionStart ?? new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000);
      const windowEnd = initiative.attributionEnd ?? new Date();
      const scopedMetrics = dailyMetrics
        .filter((metric) => metric.channel === channel && metric.metricDate >= windowStart && metric.metricDate <= windowEnd)
        .sort((a, b) => a.metricDate.getTime() - b.metricDate.getTime());
      if (scopedMetrics.length === 0) {
        continue;
      }
      const firstMetric = scopedMetrics[0];
      const lastMetric = scopedMetrics[scopedMetrics.length - 1];
      const followerDelta = lastMetric.followers - firstMetric.followers;

      await prisma.marketingInitiative.update({
        where: { id: initiative.id },
        data: {
          autoActual: followerDelta,
          autoTicketsActual: Math.max(0, Math.round(followerDelta / 10)),
          autoRevenueActual: Math.max(0, Math.round(followerDelta * 5)),
          autoUpdatedAt: new Date(),
        },
      });
    }

    const publishResult = await publishMarketingKpis({
      growthPct7d,
      engagementPct7d,
      activeInitiatives,
      syncStatus: "ready",
      instagramReach7d,
      instagramFollowers,
      weekStart: body.publishWeekStart,
    });

    await completeCaptureAutomationRunSuccess({
      runId: captureRun.id,
      department: DepartmentKey.marketing,
      weekStart: publishResult.weekStart,
      source: captureRun.source,
      sectionKey: publishResult.sectionKey,
      rowsTouched: publishResult.rowCount,
    });

    return NextResponse.json({
      ok: true,
      captureRunId: captureRun.id,
      rowsIngested: metrics.length,
      socialInsights,
      publishResult,
    });
  } catch (error) {
    const message = error instanceof Error ? error.message : "Meta sync failed.";

    await completeCaptureAutomationRunFailure({
      runId: captureRun.id,
      message,
    });

    await prisma.marketingSyncRun.update({
      where: { id: syncRun.id },
      data: {
        status: MarketingSyncRunStatus.failed,
        message,
        completedAt: new Date(),
      },
    });

    return NextResponse.json({ error: message }, { status: 400 });
  }
}
