import type { AdminState, AnalyticsEvent, LeadEvent, LeadPriority, LeadStatus } from "./mpc-types";

export const leadCrmStatuses: LeadStatus[] = ["new", "contacted", "waiting-for-visitor", "quoted", "booked", "not-available", "closed"];
export const leadCrmPriorities: LeadPriority[] = ["normal", "high", "urgent"];
const closedStatuses = new Set<LeadStatus>(["booked", "not-available", "closed"]);

function parseDate(value?: string) {
  const time = Date.parse(String(value || ""));
  return Number.isFinite(time) ? time : 0;
}
function withinDays(value: string | undefined, days: number) {
  const time = parseDate(value);
  return time > 0 && time >= Date.now() - days * 24 * 60 * 60 * 1000;
}
function parseCampaign(value?: string | null) {
  try {
    const parsed = JSON.parse(String(value || "{}"));
    return parsed && typeof parsed === "object" ? parsed as Record<string, unknown> : {};
  } catch {
    return {};
  }
}
function numberValue(value: unknown, fallback = 0) {
  const number = Number(value);
  return Number.isFinite(number) ? number : fallback;
}
export function normalizeLeadStatus(value: unknown): LeadStatus {
  return leadCrmStatuses.includes(value as LeadStatus) ? value as LeadStatus : "new";
}
export function normalizeLeadPriority(value: unknown): LeadPriority {
  return leadCrmPriorities.includes(value as LeadPriority) ? value as LeadPriority : "normal";
}
export function classifyLeadType(lead: LeadEvent) {
  const raw = `${lead.category || ""} ${lead.source || ""} ${lead.buttonType || ""} ${lead.page || ""}`.toLowerCase();
  if (raw.includes("contact") || raw.includes("property-signup") || raw.includes("accommodation-contact")) return "contact application";
  if (raw.includes("business") || raw.includes("supplier-onboarding") || raw.includes("advertise-local")) return "business onboarding";
  if (raw.includes("stay")) return "stay enquiry";
  if (raw.includes("accommodation")) return "accommodation enquiry";
  if (raw.includes("gated-email-delivery") || raw.includes("prelaunch-visitor-vault")) return "guide set";
  if (raw.includes("route") || raw.includes("pack")) return "route pack";
  if (raw.includes("guide") || raw.includes("download")) return "free guide";
  if (raw.includes("order") || raw.includes("checkout") || raw.includes("payment")) return "order / pack";
  if (raw.includes("calculator") || raw.includes("trip-finder") || raw.includes("smart-calculator")) return "trip finder";
  if (raw.includes("contact") || raw.includes("email-message")) return "contact";
  if (raw.includes("listing-view") || raw.includes("featured-impression") || raw.includes("website") || raw.includes("map") || raw.includes("booking-link")) return "engagement click";
  return "other";
}
export function leadDisplayLabel(lead: LeadEvent) {
  return lead.leadCode || lead.selectedBundleTitle || lead.routePackTitle || lead.accommodationName || lead.supplierName || lead.guideTitle || lead.contactName || lead.buttonType || lead.id;
}
export function estimatedLeadValue(lead: LeadEvent) {
  const type = classifyLeadType(lead);
  const campaign = parseCampaign(lead.campaign);
  const fromCampaign = numberValue(campaign.value ?? campaign.estimatedValue ?? campaign.orderValue, 0);
  if (fromCampaign > 0) return Math.round(fromCampaign);
  if ((lead.convertedValue || 0) > 0) return Math.round(lead.convertedValue || 0);
  if (type === "contact application") return 1500;
  if (type === "business onboarding") return 750;
  if (type === "accommodation enquiry" || type === "stay enquiry") return 250;
  if (type === "order / pack") return 99;
  if (type === "guide set") return 65;
  if (type === "route pack") return 75;
  if (type === "trip finder") return 50;
  if (type === "free guide") return 15;
  if (type === "contact") return 35;
  return 5;
}

export function publicAnalyticsEvent(event: AnalyticsEvent) {
  return {
    id: event.id,
    createdAt: event.createdAt,
    eventType: event.eventType || "unknown",
    page: event.page || "/",
    label: event.label || "",
    target: event.target || "",
    source: event.source || event.utmSource || "direct",
    utmSource: event.utmSource || "",
    utmMedium: event.utmMedium || "",
    utmCampaign: event.utmCampaign || "",
    productId: event.productId || "",
    supplierId: event.supplierId || "",
    supplierName: event.supplierName || "",
    accommodationId: event.accommodationId || "",
    accommodationName: event.accommodationName || "",
    sessionId: event.sessionId || "",
  };
}

function normalizePage(page: string) {
  const text = String(page || "/").trim() || "/";
  try {
    if (text.startsWith("http")) return new URL(text).pathname || "/";
  } catch {}
  return text.split("?")[0] || "/";
}

function eventLabel(event: ReturnType<typeof publicAnalyticsEvent>) {
  return event.label || event.target || event.productId || event.supplierName || event.accommodationName || event.page || event.eventType;
}

export function publicCrmLead(lead: LeadEvent) {
  const campaign = parseCampaign(lead.campaign);
  const followUpTime = parseDate(lead.followUpAt);
  const status = normalizeLeadStatus(lead.status);
  return {
    id: lead.id,
    receivedAt: lead.receivedAt,
    updatedAt: lead.updatedAt,
    status,
    priority: normalizeLeadPriority(lead.priority),
    type: classifyLeadType(lead),
    label: leadDisplayLabel(lead),
    estimatedValue: estimatedLeadValue(lead),
    due: followUpTime > 0 && !closedStatuses.has(status) && followUpTime <= Date.now() + 24 * 60 * 60 * 1000,
    overdue: followUpTime > 0 && !closedStatuses.has(status) && followUpTime < Date.now(),
    emailProblem: [lead.emailStatus, lead.confirmationStatus, lead.lastNurtureStatus, lead.nurtureStatus].some((value) => /fail|error|not sent|skipped/i.test(String(value || ""))),
    leadCode: lead.leadCode || "",
    category: lead.category || "",
    buttonType: lead.buttonType || "",
    source: lead.source || "",
    page: lead.page || "",
    campaign: lead.campaign || "",
    campaignSummary: Object.keys(campaign).slice(0, 8).map((key) => `${key}: ${String(campaign[key]).slice(0, 140)}`).join(" · "),
    contactName: lead.contactName || "",
    contactValue: lead.contactValue || "",
    travelMonth: lead.travelMonth || "",
    stayingAt: lead.stayingAt || "",
    supplierName: lead.supplierName || "",
    accommodationName: lead.accommodationName || "",
    routePackTitle: lead.routePackTitle || "",
    guideTitle: lead.guideTitle || "",
    selectedBundleSlug: lead.selectedBundleSlug || "",
    selectedBundleTitle: lead.selectedBundleTitle || "",
    visitorType: lead.visitorType || "",
    routeInterest: lead.routeInterest || "",
    familyStatus: lead.familyStatus || "",
    accommodationIntent: lead.accommodationIntent || "",
    localHelpInterest: lead.localHelpInterest || "",
    sourcePage: lead.sourcePage || "",
    emailStatus: lead.emailStatus || "",
    confirmationStatus: lead.confirmationStatus || "",
    nurtureStatus: lead.nurtureStatus || lead.lastNurtureStatus || "",
    followUpAt: lead.followUpAt || "",
    followUpNote: lead.followUpNote || "",
    assignedTo: lead.assignedTo || "",
    adminNotes: lead.adminNotes || "",
    crmUpdatedAt: lead.crmUpdatedAt || "",
  };
}
function countBy<T>(items: T[], fn: (item: T) => string, limit = 999) {
  const map = new Map<string, number>();
  for (const item of items) {
    const key = String(fn(item) || "unknown").trim() || "unknown";
    map.set(key, (map.get(key) || 0) + 1);
  }
  return [...map.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, limit).map(([key, count]) => ({ key, count }));
}
function dateKey(value?: string) {
  const time = parseDate(value);
  return time ? new Date(time).toISOString().slice(0, 10) : "unknown";
}
export function crmSummary(leads: LeadEvent[]) {
  const publicLeads = leads.map(publicCrmLead);
  const active = publicLeads.filter((lead) => !closedStatuses.has(lead.status));
  return {
    total: publicLeads.length,
    active: active.length,
    new: publicLeads.filter((lead) => lead.status === "new").length,
    contacted: publicLeads.filter((lead) => lead.status === "contacted").length,
    quoted: publicLeads.filter((lead) => lead.status === "quoted").length,
    booked: publicLeads.filter((lead) => lead.status === "booked").length,
    highPriority: publicLeads.filter((lead) => lead.priority === "high" || lead.priority === "urgent").length,
    due: publicLeads.filter((lead) => lead.due).length,
    overdue: publicLeads.filter((lead) => lead.overdue).length,
    emailProblems: publicLeads.filter((lead) => lead.emailProblem).length,
    today: publicLeads.filter((lead) => dateKey(lead.receivedAt) === new Date().toISOString().slice(0, 10)).length,
    week: publicLeads.filter((lead) => withinDays(lead.receivedAt, 7)).length,
    month: publicLeads.filter((lead) => withinDays(lead.receivedAt, 30)).length,
    activeEstimatedValue: active.reduce((total, lead) => total + lead.estimatedValue, 0),
  };
}

function listingPath(slug?: string) {
  return slug ? `/stay/${slug}` : "/stay";
}
function listingMatchesEvent(listing: { id?: string; name?: string; slug?: string }, event: ReturnType<typeof publicAnalyticsEvent>) {
  const page = normalizePage(event.page || "");
  return event.accommodationId === listing.id ||
    event.accommodationName === listing.name ||
    Boolean(listing.slug && page.includes(`/stay/${listing.slug}`));
}
function listingMatchesLead(listing: { id?: string; name?: string; slug?: string }, lead: ReturnType<typeof publicCrmLead>) {
  return lead.accommodationName === listing.name || Boolean(listing.slug && (lead.page || "").includes(listing.slug));
}
function lastActivityDate(events: { createdAt?: string }[], leads: { receivedAt?: string }[]) {
  const times = [...events.map((event) => parseDate(event.createdAt)), ...leads.map((lead) => parseDate(lead.receivedAt))].filter(Boolean).sort((a, b) => b - a);
  return times[0] ? new Date(times[0]).toISOString() : "";
}

export function adminAnalytics(state: AdminState, days = 30) {
  const rawLeads = state.leadEvents || [];
  const rawEvents = state.analyticsEvents || [];
  const allLeads = rawLeads.map(publicCrmLead).sort((a, b) => parseDate(b.receivedAt) - parseDate(a.receivedAt));
  const allEvents = rawEvents.map(publicAnalyticsEvent).sort((a, b) => parseDate(b.createdAt) - parseDate(a.createdAt));
  const periodLeads = days > 0 ? allLeads.filter((lead) => withinDays(lead.receivedAt, days)) : allLeads;
  const periodEvents = days > 0 ? allEvents.filter((event) => withinDays(event.createdAt, days)) : allEvents;
  const activeLeads = periodLeads.filter((lead) => !closedStatuses.has(lead.status));
  const booked = periodLeads.filter((lead) => lead.status === "booked");
  const accommodationListings = state.accommodationListings || [];
  const suppliers = state.suppliers || [];
  const downloads = state.downloadAccess || [];
  const accommodationEvents = periodLeads.filter((lead) => lead.type === "accommodation enquiry" || lead.accommodationName || lead.buttonType === "listing-view" || lead.buttonType === "featured-impression");
  const pageViews = periodEvents.filter((event) => event.eventType === "page_view" || event.eventType === "bundle_view" || event.eventType === "accommodation_view");
  const clickEvents = periodEvents.filter((event) => /click$|_click$|pdf_open|file_download/.test(event.eventType));
  const bundleEvents = periodEvents.filter((event) => event.eventType === "bundle_view" || event.eventType === "bundle_order_click" || event.page.includes("/bundles/"));
  const freePackEvents = periodEvents.filter((event) => event.eventType === "free_pack_download_click" || event.target.includes("mpc-free-explorer-pack") || event.label.toLowerCase().includes("guide set"));
  const localOptionEvents = periodEvents.filter((event) => event.eventType === "local_option_click" || event.eventType === "business_contact_click" || event.eventType === "business_directions_click" || event.page.includes("/local-options"));
  const recommendations = [] as { title: string; text: string }[];
  const summary = crmSummary(rawLeads.filter((lead) => days > 0 ? withinDays(lead.receivedAt, days) : true));
  if (summary.new > 0) recommendations.push({ title: "Follow up new leads", text: `${summary.new} leads are still marked new.` });
  if (summary.overdue > 0) recommendations.push({ title: "Clear overdue follow-ups", text: `${summary.overdue} follow-ups are overdue.` });
  if (summary.emailProblems > 0) recommendations.push({ title: "Check failed email delivery", text: `${summary.emailProblems} leads show an email delivery problem.` });
  if (periodEvents.length === 0) recommendations.push({ title: "Confirm page-view tracking", text: "No first-party page-view events are stored for this period yet. Visit the public pages after deployment and reload analytics." });
  if (pageViews.length > 0 && periodLeads.length === 0) recommendations.push({ title: "Improve visitor conversion", text: "Page views are being recorded, but no enquiries are recorded in this period yet. Review the Free Pack and guide calls to action." });
  if (!recommendations.length) recommendations.push({ title: "Review the strongest pages", text: "Use top page views and CTA clicks to decide which visitor paths to promote first." });
  const uniqueSessions = new Set(periodEvents.map((event) => event.sessionId).filter(Boolean)).size;
  const pageToLeadRate = pageViews.length ? Math.round((periodLeads.length / pageViews.length) * 1000) / 10 : 0;
  const listingReports = accommodationListings.map((listing) => {
    const listingEvents = periodEvents.filter((event) => listingMatchesEvent(listing, event));
    const listingLeads = periodLeads.filter((lead) => listingMatchesLead(listing, lead));
    const views = listingEvents.filter((event) => event.eventType === "page_view" || event.eventType === "accommodation_view").length;
    const featuredImpressions = listingEvents.filter((event) => event.eventType === "featured_impression" || event.label.toLowerCase().includes("featured")).length;
    const contactClicks = listingEvents.filter((event) => /contact_click|cta_click|booking|whatsapp|email|phone/i.test(`${event.eventType} ${event.label} ${event.target}`)).length;
    const enquiries = listingLeads.filter((lead) => lead.type === "accommodation enquiry" || lead.type === "stay enquiry" || lead.accommodationName).length;
    const totalActions = featuredImpressions + contactClicks + enquiries;
    const lastActionAt = lastActivityDate(listingEvents, listingLeads);
    const conversionRate = views ? Math.round((enquiries / views) * 1000) / 10 : 0;
    const publicFlowStatus = views === 0
      ? "Published but no recorded public views in this period"
      : enquiries > 0
        ? "Views and enquiries recorded"
        : contactClicks > 0
          ? "Views and contact actions recorded"
          : "Views recorded; no enquiry yet";
    const nextAction = enquiries > 0
      ? "Send contact report and confirm enquiry handling."
      : contactClicks > 0
        ? "Check contact path and ask contact if enquiries arrived directly."
        : views > 0
          ? "Review photos, title and call-to-action for stronger conversion."
          : listing.status === "active"
            ? "Confirm tracking after deployment and drive traffic to this listing."
            : "Listing is not active; complete contact/publication workflow.";
    return {
      id: listing.id,
      name: listing.name,
      slug: listing.slug,
      tier: listing.tier,
      status: listing.status,
      ownerName: listing.ownerName || "",
      ownerEmail: listing.ownerEmail || "",
      pagePath: listingPath(listing.slug),
      views,
      featuredImpressions,
      contactClicks,
      enquiries,
      totalActions,
      conversionRate,
      lastActionAt,
      publicFlowStatus,
      nextAction,
      reportNotes: listing.ownerReportNotes || "",
    };
  }).sort((a, b) => b.totalActions - a.totalActions || b.views - a.views || a.name.localeCompare(b.name));
  return {
    generatedAt: new Date().toISOString(),
    periodDays: days,
    summary: {
      leads: periodLeads.length,
      activeLeads: activeLeads.length,
      booked: booked.length,
      conversionRate: periodLeads.length ? Math.round((booked.length / periodLeads.length) * 1000) / 10 : 0,
      pageToLeadRate,
      estimatedPipelineValue: activeLeads.reduce((total, lead) => total + lead.estimatedValue, 0),
      estimatedBookedValue: booked.reduce((total, lead) => lead.estimatedValue + total, 0),
      dueFollowUps: periodLeads.filter((lead) => lead.due).length,
      overdueFollowUps: periodLeads.filter((lead) => lead.overdue).length,
      emailProblems: periodLeads.filter((lead) => lead.emailProblem).length,
      publicAccommodationListings: accommodationListings.filter((item) => item.status === "active").length,
      pausedAccommodationListings: accommodationListings.filter((item) => item.status && item.status !== "active").length,
      publicSuppliers: suppliers.filter((item) => item.status === "live").length,
      downloadAccessRecords: downloads.length,
      analyticsEvents: periodEvents.length,
      pageViews: pageViews.length,
      uniqueSessions,
      ctaClicks: periodEvents.filter((event) => event.eventType === "cta_click").length,
      pdfOpens: periodEvents.filter((event) => event.eventType === "pdf_open").length,
      fileDownloads: periodEvents.filter((event) => event.eventType === "file_download").length,
      freePackActions: freePackEvents.length,
      bundleViews: periodEvents.filter((event) => event.eventType === "bundle_view").length,
      bundleOrderClicks: periodEvents.filter((event) => event.eventType === "bundle_order_click").length,
      localOptionActions: localOptionEvents.length,
      partnerContactClicks: periodEvents.filter((event) => event.eventType === "business_contact_click" || event.eventType === "business_directions_click").length,
      formSubmits: periodEvents.filter((event) => event.eventType === "form_submit").length,
    },
    counts: {
      byStatus: countBy(periodLeads, (lead) => lead.status),
      byPriority: countBy(periodLeads, (lead) => lead.priority),
      byType: countBy(periodLeads, (lead) => lead.type),
      bySource: countBy(periodLeads, (lead) => lead.source || lead.buttonType || "direct/unknown", 12),
      byPage: countBy(periodLeads, (lead) => lead.page || "unknown", 15),
      byDay: countBy(periodLeads, (lead) => dateKey(lead.receivedAt), 90).sort((a, b) => a.key.localeCompare(b.key)),
      accommodationInterest: countBy(accommodationEvents, (lead) => lead.accommodationName || lead.supplierName || lead.page || "unknown", 12),
      byEventType: countBy(periodEvents, (event) => event.eventType, 20),
      pageViewsByPage: countBy(pageViews, (event) => normalizePage(event.page), 30),
      clicksByLabel: countBy(clickEvents, eventLabel, 30),
      trafficSources: countBy(periodEvents, (event) => event.utmSource || event.source || "direct", 20),
      campaignSources: countBy(periodEvents, (event) => [event.utmSource, event.utmMedium, event.utmCampaign].filter(Boolean).join(" / ") || event.source || "direct", 20),
      eventVolumeByDay: countBy(periodEvents, (event) => dateKey(event.createdAt), 90).sort((a, b) => a.key.localeCompare(b.key)),
      bundleInterest: countBy(bundleEvents, (event) => event.productId || normalizePage(event.page), 12),
      freePackActivity: countBy(freePackEvents, eventLabel, 12),
      localOptionActions: countBy(localOptionEvents, (event) => event.supplierName || event.label || event.target || event.page, 20),
      downloadActivity: countBy(periodEvents.filter((event) => event.eventType === "file_download" || event.eventType === "pdf_open" || event.eventType === "free_pack_download_click"), (event) => event.productId || event.label || event.target || event.page, 20),
    },
    emailHealth: {
      internalSent: periodLeads.filter((lead) => /sent|ok|processed/i.test(String(lead.emailStatus || ""))).length,
      confirmationsSent: periodLeads.filter((lead) => /sent|ok|processed/i.test(String(lead.confirmationStatus || ""))).length,
      nurtureActive: periodLeads.filter((lead) => lead.nurtureStatus || (rawLeads.find((raw) => raw.id === lead.id) || {}).nurture).length,
      problems: periodLeads.filter((lead) => lead.emailProblem).slice(0, 25),
    },
    listingReports,
    recommendations: recommendations.slice(0, 5),
    recentLeads: periodLeads.slice(0, 25),
    recentEvents: periodEvents.slice(0, 50),
  };
}
