/**
 * Channel Classifier
 * ══════════════════
 * Rule-based classifier that maps a SiteConversion row to a canonical
 * channel. Priority: UTM params → referrer domain → landing page →
 * existing firstTouchChannel → fallback.
 *
 * "Other" should be < 10% per brand with proper rules. When it's
 * higher, the Signal generator fires a data_quality warning.
 */

// ─── Taxonomy ──────────────────────────────────────────────────────

export const CHANNELS = {
  organic_search: "Organic Search",
  paid_search: "Paid Search",
  paid_social: "Paid Social",
  organic_social: "Organic Social",
  email: "Email",
  referral: "Referral",
  direct: "Direct",
  display: "Display",
  affiliate: "Affiliate",
  local_maps: "Local / Maps",
  ai_assistant: "AI Assistant",
  video: "Video",
  internal: "Internal",
  other: "Other",
} as const;

export type ChannelKey = keyof typeof CHANNELS;

export const CHANNEL_COLORS: Record<ChannelKey, string> = {
  organic_search: "#10b981",
  paid_search: "#ef4444",
  paid_social: "#f43f5e",
  organic_social: "#3b82f6",
  email: "#8b5cf6",
  referral: "#f59e0b",
  direct: "#94a3b8",
  display: "#ec4899",
  affiliate: "#14b8a6",
  local_maps: "#f97316",
  ai_assistant: "#6366f1",
  video: "#e11d48",
  internal: "#cbd5e1",
  other: "#9ca3af",
};

export interface ClassificationResult {
  channel: ChannelKey;
  confidence: "high" | "medium" | "low";
  reasonCode: string;
  evidence: Record<string, string | null>;
}

// ─── Rule sets ─────────────────────────────────────────────────────

const UTM_MEDIUM_MAP: Array<{ patterns: string[]; channel: ChannelKey }> = [
  { patterns: ["cpc", "ppc", "paidsearch", "paid-search", "sem", "adwords", "google_ads"], channel: "paid_search" },
  { patterns: ["paidsocial", "paid-social", "cpm", "social-paid", "fbads", "meta_ads"], channel: "paid_social" },
  { patterns: ["email", "newsletter", "e-mail", "email-marketing"], channel: "email" },
  { patterns: ["affiliate", "aff"], channel: "affiliate" },
  { patterns: ["display", "banner", "cpm-display", "programmatic"], channel: "display" },
  { patterns: ["video", "youtube"], channel: "video" },
];

const ORGANIC_SEARCH_DOMAINS = [
  "google.", "bing.com", "duckduckgo.com", "yahoo.com", "baidu.com",
  "yandex.", "ecosia.org", "brave.com/search", "search.aol.com",
];

const AI_ASSISTANT_DOMAINS = [
  "chat.openai.com", "chatgpt.com", "claude.ai", "perplexity.ai",
  "gemini.google.com", "bard.google.com", "copilot.microsoft.com",
  "you.com", "phind.com", "meta.ai", "poe.com",
];

const SOCIAL_DOMAINS = [
  "facebook.com", "fb.com", "instagram.com", "linkedin.com",
  "twitter.com", "x.com", "tiktok.com", "pinterest.com",
  "reddit.com", "threads.net", "mastodon.",
];

const VIDEO_DOMAINS = ["youtube.com", "youtu.be", "vimeo.com", "dailymotion.com"];

const EMAIL_DOMAINS = [
  "mail.google.com", "outlook.live.com", "outlook.office.com",
  "mail.yahoo.com", "webmail.", "mailchimp.com", "campaign-archive.com",
  "constantcontact.com", "klaviyo.com", "sendgrid.net",
];

const LOCAL_MAPS_PATTERNS = [
  "maps.google", "google.com/maps", "google.com/local",
  "business.google.com",
];

const PAID_CLICK_IDS = ["gclid", "fbclid", "msclkid", "ttclid", "li_fat_id", "dclid", "wbraid", "gbraid"];

function domainMatches(referrer: string, domains: string[]): boolean {
  const lower = referrer.toLowerCase();
  return domains.some((d) => lower.includes(d));
}

// ─── Classifier ────────────────────────────────────────────────────

export interface ConversionInput {
  firstTouchChannel: string | null;
  firstTouchSource: string | null;
  lastTouchChannel: string | null;
  lastTouchSource: string | null;
  pageUrl: string;
  touchpoints: unknown;
}

export function classifyChannel(
  input: ConversionInput,
  brandOverrides?: Array<{ pattern: string; field: string; channel: ChannelKey }>,
): ClassificationResult {
  const source = (input.firstTouchSource ?? "").toLowerCase();
  const channel = (input.firstTouchChannel ?? "").toLowerCase();
  const referrer = source; // firstTouchSource typically carries the referrer
  const pageUrl = (input.pageUrl ?? "").toLowerCase();

  // Extract UTM-like params from source or channel
  const utmMedium = channel || "";

  const evidence: Record<string, string | null> = {
    firstTouchSource: input.firstTouchSource,
    firstTouchChannel: input.firstTouchChannel,
    pageUrl: input.pageUrl,
  };

  // 0. Brand-specific overrides (highest priority)
  if (brandOverrides) {
    for (const rule of brandOverrides) {
      const field = rule.field === "source" ? source : rule.field === "channel" ? channel : pageUrl;
      if (field.includes(rule.pattern.toLowerCase())) {
        return { channel: rule.channel, confidence: "high", reasonCode: "brand_override", evidence };
      }
    }
  }

  // 1. UTM-based classification
  for (const rule of UTM_MEDIUM_MAP) {
    if (rule.patterns.some((p) => utmMedium.includes(p))) {
      return { channel: rule.channel, confidence: "high", reasonCode: `utm_medium:${utmMedium}`, evidence };
    }
  }

  // 2. Paid click ID detection (gclid → paid_search, fbclid → paid_social)
  if (pageUrl.includes("gclid=") || source.includes("gclid")) {
    return { channel: "paid_search", confidence: "high", reasonCode: "gclid_detected", evidence };
  }
  if (pageUrl.includes("fbclid=") || source.includes("fbclid")) {
    return { channel: "paid_social", confidence: "high", reasonCode: "fbclid_detected", evidence };
  }
  if (pageUrl.includes("msclkid=")) {
    return { channel: "paid_search", confidence: "high", reasonCode: "msclkid_detected", evidence };
  }

  // 3. AI assistant referrer
  if (domainMatches(referrer, AI_ASSISTANT_DOMAINS)) {
    return { channel: "ai_assistant", confidence: "high", reasonCode: "ai_referrer", evidence };
  }

  // 4. Local maps
  if (domainMatches(referrer, LOCAL_MAPS_PATTERNS) || channel === "local" || channel === "maps") {
    return { channel: "local_maps", confidence: "high", reasonCode: "maps_referrer", evidence };
  }

  // 5. Email
  if (domainMatches(referrer, EMAIL_DOMAINS) || utmMedium === "email" || channel === "email") {
    return { channel: "email", confidence: "high", reasonCode: "email_referrer", evidence };
  }

  // 6. Video
  if (domainMatches(referrer, VIDEO_DOMAINS)) {
    return { channel: "video", confidence: "high", reasonCode: "video_referrer", evidence };
  }

  // 7. Social (organic by default — paid would have been caught by UTM/fbclid above)
  if (domainMatches(referrer, SOCIAL_DOMAINS)) {
    return { channel: "organic_social", confidence: "high", reasonCode: "social_referrer", evidence };
  }

  // 8. Organic search
  if (domainMatches(referrer, ORGANIC_SEARCH_DOMAINS) || channel === "organic search" || channel === "organic") {
    return { channel: "organic_search", confidence: "high", reasonCode: "search_referrer", evidence };
  }

  // 9. Known channel values from Site Tag
  if (channel === "paid search" || channel === "paid_search") return { channel: "paid_search", confidence: "medium", reasonCode: "site_tag_channel", evidence };
  if (channel === "paid social" || channel === "paid_social") return { channel: "paid_social", confidence: "medium", reasonCode: "site_tag_channel", evidence };
  if (channel === "referral" && referrer) return { channel: "referral", confidence: "medium", reasonCode: "site_tag_referral", evidence };
  if (channel === "display") return { channel: "display", confidence: "medium", reasonCode: "site_tag_channel", evidence };
  if (channel === "affiliate") return { channel: "affiliate", confidence: "medium", reasonCode: "site_tag_channel", evidence };

  // 10. Has a referrer but didn't match any known domain → referral
  if (referrer && referrer.length > 3 && !referrer.startsWith("(") && referrer !== "direct") {
    return { channel: "referral", confidence: "low", reasonCode: "unknown_referrer", evidence };
  }

  // 11. Direct (no referrer, no UTM, no channel)
  if ((!referrer || referrer === "direct" || referrer === "(direct)") && !utmMedium) {
    return { channel: "direct", confidence: "medium", reasonCode: "no_referrer", evidence };
  }

  // 12. Fallback
  return { channel: "other", confidence: "low", reasonCode: "unclassified", evidence };
}
