import type {Granularity, PeriodPoint, RewardUsagePoint, StoreComparison} from '@/features/dashboard/types/dashboard';
import type {RangeKey} from '@/shared/types/api';
import {createRng} from '@/shared/mock/rng';
import {buildTimeseries} from './dashboard.mock';
import {STORE_SEED} from '@/features/stores/mock/stores.mock';

/**
 * Per-store totals for the comparison small-multiples.
 *
 * Built from the SAME buildTimeseries() the main charts use, so a store's
 * comparison bar and its own dashboard cannot disagree — the alternative
 * (independent generators) produces fixtures that quietly contradict each
 * other and make real bugs impossible to spot.
 */
export function buildStoreComparison(
  range: RangeKey,
  endMs: number,
): StoreComparison[] {
  return STORE_SEED.map((s) => {
    const points = buildTimeseries(range, s.id, endMs);
    const visitors = points.reduce((a, p) => a + p.visitors, 0);
    const purchases = points.reduce((a, p) => a + p.purchases, 0);
    return {
      storeId: s.id,
      name: s.name,
      visitors,
      purchases,
      revenueInr: points.reduce((a, p) => a + p.revenue, 0),
      conversionPct: visitors
        ? Number(((purchases / visitors) * 100).toFixed(1))
        : 0,
      trend: points.slice(-14).map((p) => ({t: p.t, v: p.visitors})),
    };
  });
}

const REWARDS = [
  {id: 'r-coffee', name: 'Free Coffee'},
  {id: 'r-combo20', name: 'Combo 20% off'},
  {id: 'r-weekend', name: 'Weekend Bonus'},
  {id: 'r-birthday', name: 'Birthday Treat'},
  {id: 'r-bogo', name: 'Buy 1 Get 1'},
  {id: 'r-dessert', name: 'Dessert on us'},
];

/**
 * Claim-vs-use. The gap between the two is the actual story on this chart:
 * a reward claimed 800 times and used 90 is a liability, not a success, and
 * the fixtures are shaped so that story is visible.
 */
export function buildRewardUsage(
  storeId: string,
  range: RangeKey,
): RewardUsagePoint[] {
  return REWARDS.map((r, i) => {
    const rng = createRng('reward-usage', r.id, storeId, range);
    const claimed = Math.round(rng.int(120, 900) * (storeId === 'all' ? 1 : 0.3));
    // Redemption rates vary widely by reward type — deliberately spread so
    // "most claimed" and "least used" are different rewards.
    const rate = [0.72, 0.34, 0.81, 0.55, 0.19, 0.44][i];
    const used = Math.round(claimed * rate * rng.float(0.9, 1.1));
    return {
      rewardId: r.id,
      name: r.name,
      claimed,
      used,
      usageRatePct: claimed ? Number(((used / claimed) * 100).toFixed(1)) : 0,
    };
  }).sort((a, b) => b.claimed - a.claimed);
}

const MONTHS = [
  'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
];

/**
 * Weekly / monthly rollup behind the SegmentedControl.
 *
 * Buckets are derived from the daily series rather than generated fresh, so
 * "last 30 days" and "the weeks inside it" always sum to the same totals.
 */
export function buildPeriodPerformance(
  granularity: Granularity,
  storeId: string,
  endMs: number,
): PeriodPoint[] {
  // Always pull a long window; the granularity decides how it is grouped.
  const range: RangeKey = granularity === 'weekly' ? '90d' : 'ytd';
  const points = buildTimeseries(range, storeId, endMs);

  const buckets = new Map<string, PeriodPoint>();
  for (const p of points) {
    const d = new Date(p.t);
    const label =
      granularity === 'weekly'
        ? `W${isoWeek(d)}`
        : MONTHS[d.getUTCMonth()];

    const existing = buckets.get(label) ?? {
      label,
      visitors: 0,
      purchases: 0,
      revenue: 0,
    };
    existing.visitors += p.visitors;
    existing.purchases += p.purchases;
    existing.revenue += p.revenue;
    buckets.set(label, existing);
  }

  const out = [...buckets.values()];
  // Drop the leading bucket: a partial first week or month renders as a
  // collapsed bar and reads as a crash rather than as missing days.
  return out.length > 2 ? out.slice(1) : out;
}

function isoWeek(d: Date): number {
  const target = new Date(
    Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()),
  );
  const dayNum = (target.getUTCDay() + 6) % 7;
  target.setUTCDate(target.getUTCDate() - dayNum + 3);
  const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
  const firstDayNum = (firstThursday.getUTCDay() + 6) % 7;
  firstThursday.setUTCDate(firstThursday.getUTCDate() - firstDayNum + 3);
  return (
    1 + Math.round((target.getTime() - firstThursday.getTime()) / 604800000)
  );
}
