# Generates realistic analytics demo data spanning 2 years.
#
#     mix run priv/repo/seeds/analytics.exs
#
# Clears existing analytics events first, then creates ~90k events with
# realistic traffic patterns, referrers, device mix, and e-commerce funnel.

alias Berrypod.Repo
alias Berrypod.Analytics.Event

# ── Config ──

# How many unique "visitors" to simulate per day (base — actual varies by day)
base_daily_visitors = 40

# Date range: 2 years back from today
end_date = Date.utc_today()
start_date = Date.add(end_date, -729)

# ── Reference data ──

# Shop pages and their relative popularity (weights)
pages = [
  {"/", 30},
  {"/collections/t-shirts", 15},
  {"/collections/hoodies", 12},
  {"/collections/mugs", 10},
  {"/collections/stickers", 8},
  {"/products/1", 8},
  {"/products/2", 7},
  {"/products/3", 6},
  {"/products/4", 5},
  {"/products/5", 5},
  {"/products/6", 4},
  {"/products/7", 3},
  {"/products/8", 3},
  {"/products/9", 2},
  {"/products/10", 2},
  {"/about", 4},
  {"/delivery", 3},
  {"/contact", 2},
  {"/privacy", 1},
  {"/terms", 1},
  {"/cart", 6}
]

# Referrer sources with weights (nil = direct traffic)
sources = [
  {nil, nil, 35},
  {"google.com", "Google", 25},
  {"instagram.com", "Instagram", 10},
  {"facebook.com", "Facebook", 8},
  {"tiktok.com", "TikTok", 6},
  {"twitter.com", "Twitter", 4},
  {"pinterest.com", "Pinterest", 4},
  {"reddit.com", "Reddit", 3},
  {"youtube.com", "YouTube", 2},
  {"bing.com", "Bing", 1},
  {"etsy.com", nil, 1},
  {"blogpost.example.com", nil, 1}
]

# UTM campaigns (only applied to social/search traffic, not direct)
campaigns = [
  {nil, nil, nil, 60},
  {"instagram", "social", "summer_sale", 10},
  {"facebook", "cpc", "retarget_q4", 8},
  {"google", "cpc", "brand_terms", 7},
  {"tiktok", "social", "viral_hoodie", 5},
  {"newsletter", "email", "weekly_digest", 5},
  {"pinterest", "social", "pin_collection", 3},
  {"twitter", "social", "launch_promo", 2}
]

# Countries with weights (UK-focused shop)
countries = [
  {"GB", 40},
  {"US", 15},
  {"DE", 7},
  {"FR", 5},
  {"CA", 4},
  {"AU", 4},
  {"NL", 3},
  {"IE", 3},
  {"SE", 2},
  {"IT", 2},
  {"ES", 2},
  {"BE", 2},
  {"NO", 1},
  {"DK", 1},
  {"PL", 1},
  {"CH", 1},
  {"NZ", 1},
  {"JP", 1},
  {"IN", 2},
  {"BR", 2},
  {"PT", 1}
]

# Browsers (matches UAParser output)
browsers = [
  {"Chrome", 55},
  {"Safari", 25},
  {"Firefox", 10},
  {"Edge", 8},
  {"Opera", 2}
]

# Operating systems (matches UAParser output)
oses = [
  {"iOS", 30},
  {"Android", 25},
  {"Windows", 22},
  {"macOS", 15},
  {"Linux", 5},
  {"ChromeOS", 3}
]

# Screen sizes (matches classify_screen/1 in AnalyticsHook)
screen_sizes = [
  {"mobile", 55},
  {"tablet", 12},
  {"desktop", 33}
]

# ── Helpers ──

# Weighted random pick from a list of {item, weight} or {a, b, weight} tuples
weighted_pick = fn items ->
  total = items |> Enum.map(&elem(&1, tuple_size(&1) - 1)) |> Enum.sum()
  roll = :rand.uniform() * total

  Enum.reduce_while(items, 0.0, fn item, acc ->
    weight = elem(item, tuple_size(item) - 1)
    acc = acc + weight
    if acc >= roll, do: {:halt, item}, else: {:cont, acc}
  end)
end

# Day-of-week multiplier (weekends get more traffic for a consumer shop)
day_multiplier = fn date ->
  case Date.day_of_week(date) do
    6 -> 1.3
    7 -> 1.2
    1 -> 0.85
    _ -> 1.0
  end
end

# Monthly growth curve — traffic grows over time (new shop ramping up)
month_multiplier = fn date ->
  months_ago = Date.diff(end_date, date) / 30.0
  # Start at 0.2x two years ago and grow to 1.0x now
  max(0.2, 1.0 - months_ago * 0.035)
end

# Seasonal bumps (Nov-Dec holiday shopping, Jan sale)
seasonal_multiplier = fn date ->
  case date.month do
    11 -> 1.4
    12 -> 1.8
    1 -> 1.3
    6 -> 1.1
    7 -> 1.1
    _ -> 1.0
  end
end

# Random time of day (weighted toward daytime UK hours)
random_time = fn ->
  # Bell curve centered around 14:00 UTC (afternoon UK time)
  hour = min(23, max(0, round(:rand.normal(14.0, 4.0))))
  minute = :rand.uniform(60) - 1
  second = :rand.uniform(60) - 1
  Time.new!(hour, minute, second)
end

# ── Clear existing data ──

IO.puts("Clearing existing analytics events...")
{deleted, _} = Repo.delete_all(Event)
IO.puts("  Deleted #{deleted} existing events")

# ── Generate events ──

IO.puts("Generating analytics data from #{start_date} to #{end_date}...")

dates = Date.range(start_date, end_date)

all_events =
  Enum.flat_map(dates, fn date ->
    # Calculate visitor count for this day
    base = base_daily_visitors

    multiplied =
      base * day_multiplier.(date) * month_multiplier.(date) * seasonal_multiplier.(date)

    # Add some randomness (+/- 20%)
    jitter = 0.8 + :rand.uniform() * 0.4
    visitor_count = round(multiplied * jitter)

    # Generate visitors for this day
    Enum.flat_map(1..visitor_count, fn _v ->
      visitor_hash = :crypto.strong_rand_bytes(8)
      session_hash = :crypto.strong_rand_bytes(8)

      # Pick visitor attributes (consistent within a visit)
      {referrer, referrer_source, _} = weighted_pick.(sources)
      {country_code, _} = weighted_pick.(countries)
      {browser, _} = weighted_pick.(browsers)
      {os, _} = weighted_pick.(oses)
      {screen_size, _} = weighted_pick.(screen_sizes)

      # Maybe assign UTM params (only if has a referrer source)
      {utm_source, utm_medium, utm_campaign} =
        if referrer_source do
          {src, med, camp, _} = weighted_pick.(campaigns)
          {src, med, camp}
        else
          {nil, nil, nil}
        end

      # How many pages does this visitor view? (1-6, weighted toward fewer)
      page_count = min(6, max(1, round(:rand.normal(2.0, 1.2))))

      # Pick pages for this session
      session_pages =
        Enum.map(1..page_count, fn _ ->
          {page, _} = weighted_pick.(pages)
          page
        end)

      # Generate pageview events with increasing timestamps
      base_time = random_time.()
      base_dt = DateTime.new!(date, base_time, "Etc/UTC")

      pageview_events =
        session_pages
        |> Enum.with_index()
        |> Enum.map(fn {pathname, i} ->
          # Each subsequent page is 15-120 seconds later
          offset = if i == 0, do: 0, else: Enum.sum(for _ <- 1..i, do: 15 + :rand.uniform(105))
          ts = DateTime.add(base_dt, offset, :second)

          [
            id: Ecto.UUID.generate(),
            name: "pageview",
            pathname: pathname,
            visitor_hash: visitor_hash,
            session_hash: session_hash,
            referrer: referrer,
            referrer_source: referrer_source,
            utm_source: utm_source,
            utm_medium: utm_medium,
            utm_campaign: utm_campaign,
            country_code: country_code,
            browser: browser,
            os: os,
            screen_size: screen_size,
            revenue: nil,
            inserted_at: DateTime.truncate(ts, :second)
          ]
        end)

      # E-commerce funnel events (progressive drop-off)
      # Only trigger if visitor viewed a product page
      viewed_product = Enum.any?(session_pages, &String.starts_with?(&1, "/products/"))

      ecommerce_events =
        if viewed_product do
          product_path = Enum.find(session_pages, &String.starts_with?(&1, "/products/"))
          last_ts = pageview_events |> List.last() |> Keyword.get(:inserted_at)

          base_attrs = [
            visitor_hash: visitor_hash,
            session_hash: session_hash,
            referrer: referrer,
            referrer_source: referrer_source,
            utm_source: utm_source,
            utm_medium: utm_medium,
            utm_campaign: utm_campaign,
            country_code: country_code,
            browser: browser,
            os: os,
            screen_size: screen_size
          ]

          # product_view: 100% of product page viewers
          product_view = [
            [
              id: Ecto.UUID.generate(),
              name: "product_view",
              pathname: product_path,
              revenue: nil,
              inserted_at: DateTime.add(last_ts, 5, :second)
            ] ++ base_attrs
          ]

          # add_to_cart: ~35% of product viewers
          add_to_cart =
            if :rand.uniform() < 0.35 do
              [
                [
                  id: Ecto.UUID.generate(),
                  name: "add_to_cart",
                  pathname: product_path,
                  revenue: nil,
                  inserted_at: DateTime.add(last_ts, 30, :second)
                ] ++ base_attrs
              ]
            else
              []
            end

          # checkout_start: ~60% of add-to-carters
          checkout_start =
            if add_to_cart != [] and :rand.uniform() < 0.60 do
              [
                [
                  id: Ecto.UUID.generate(),
                  name: "checkout_start",
                  pathname: "/cart",
                  revenue: nil,
                  inserted_at: DateTime.add(last_ts, 60, :second)
                ] ++ base_attrs
              ]
            else
              []
            end

          # purchase: ~70% of checkout starters
          purchase =
            if checkout_start != [] and :rand.uniform() < 0.70 do
              # Revenue between 1500 (GBP 15.00) and 8500 (GBP 85.00)
              revenue = 1500 + :rand.uniform(7000)

              [
                [
                  id: Ecto.UUID.generate(),
                  name: "purchase",
                  pathname: "/checkout/success",
                  revenue: revenue,
                  inserted_at: DateTime.add(last_ts, 120, :second)
                ] ++ base_attrs
              ]
            else
              []
            end

          product_view ++ add_to_cart ++ checkout_start ++ purchase
        else
          []
        end

      pageview_events ++ ecommerce_events
    end)
  end)

# ── Batch insert ──

total = length(all_events)
IO.puts("Inserting #{total} events in batches...")

all_events
|> Enum.chunk_every(1000)
|> Enum.with_index(1)
|> Enum.each(fn {batch, i} ->
  Repo.insert_all(Event, batch)
  progress = min(i * 1000, total)
  IO.write("\r  #{progress}/#{total}")
end)

IO.puts("\n  Done!")

# ── Summary stats ──

pageviews = Enum.count(all_events, fn e -> Keyword.get(e, :name) == "pageview" end)
product_views = Enum.count(all_events, fn e -> Keyword.get(e, :name) == "product_view" end)
add_to_carts = Enum.count(all_events, fn e -> Keyword.get(e, :name) == "add_to_cart" end)
checkouts = Enum.count(all_events, fn e -> Keyword.get(e, :name) == "checkout_start" end)
purchases = Enum.count(all_events, fn e -> Keyword.get(e, :name) == "purchase" end)

total_revenue =
  all_events
  |> Enum.filter(fn e -> Keyword.get(e, :name) == "purchase" end)
  |> Enum.map(fn e -> Keyword.get(e, :revenue) end)
  |> Enum.sum()

IO.puts("""

Summary:
  Pageviews:     #{pageviews}
  Product views: #{product_views}
  Add to cart:   #{add_to_carts}
  Checkouts:     #{checkouts}
  Purchases:     #{purchases}
  Revenue:       GBP #{Float.round(total_revenue / 100, 2)}
""")
