import type {Attendance, AttendancePoint, StaffMember, StaffSummary} from '@/features/staff/types/staff';
import type {RangeKey} from '@/shared/types/api';
import {createRng} from '@/shared/mock/rng';
import {STORE_SEED} from '@/features/stores/mock/stores.mock';
import {RANGE_DAYS} from '@/features/dashboard/mock/dashboard.mock';

const FIRST = [
  'Anita', 'Vikram', 'Priya', 'Rahul', 'Deepa', 'Suresh', 'Meera', 'Arjun',
  'Kavya', 'Nikhil', 'Sneha', 'Rohit', 'Divya', 'Karthik', 'Ananya', 'Manish',
  'Lakshmi', 'Sanjay', 'Pooja', 'Ravi', 'Nisha', 'Ajay', 'Shreya', 'Vinod',
];
const LAST = [
  'R', 'S', 'M', 'K', 'N', 'T', 'P', 'V', 'B', 'G', 'J', 'D',
];
const ROLES = [
  'Store manager',
  'Shift lead',
  'Sales associate',
  'Sales associate',
  'Cashier',
];

/**
 * The roster is sized from each store's own staffCount, so the Staff module
 * and a store's card cannot disagree about how many people work there — the
 * store card literally says "14 staff" and this must produce 14 rows.
 */
/**
 * No `endMs` parameter, unlike the other builders: a roster is not a time
 * series. Who works at a store depends on the store and the period length,
 * not on which day you ask.
 */
export function buildStaff(storeId: string, range: RangeKey): StaffMember[] {
  const stores =
    storeId === 'all'
      ? STORE_SEED
      : STORE_SEED.filter((s) => s.id === storeId);

  const out: StaffMember[] = [];
  let nameIdx = 0;

  for (const store of stores) {
    for (let i = 0; i < store.staffCount; i++) {
      const rng = createRng('staff', store.id, i, range);
      const first = FIRST[nameIdx % FIRST.length];
      const last = LAST[(nameIdx * 7 + i) % LAST.length];
      nameIdx += 1;

      // Attendance is weighted, not uniform: a roster where a fifth of the
      // team is absent every day would be a business in crisis, not a normal
      // Tuesday. Most people are present.
      const roll = rng.next();
      const attendance: Attendance =
        store.status === 'closed'
          ? 'absent'
          : roll < 0.72
            ? 'present'
            : roll < 0.85
              ? 'late'
              : roll < 0.94
                ? 'absent'
                : 'leave';

      // Sales track the store's own traffic, so a flagship associate
      // outsells a quiet-branch associate — which is what makes the
      // leaderboard mean anything.
      const salesCount = Math.round(
        rng.int(18, 64) * store.scale * (RANGE_DAYS[range] / 30),
      );
      const punctualityPct = Number(rng.float(72, 99).toFixed(1));

      out.push({
        id: `${store.id}-${i}`,
        name: `${first} ${last}`,
        avatarUrl: null,
        storeId: store.id,
        role: i === 0 ? ROLES[0] : ROLES[1 + (i % (ROLES.length - 1))],
        attendance,
        salesCount,
        rewardsIssued: Math.round(salesCount * rng.float(0.3, 0.7)),
        // Weighted toward sales, then punctuality — a scorecard that is
        // mostly attendance rewards showing up over selling.
        performanceScore: Math.round(
          Math.min(
            100,
            (salesCount / (store.scale * 2)) * 0.7 + punctualityPct * 0.3,
          ),
        ),
        punctualityPct,
      });
    }
  }

  return out;
}

export function buildStaffSummary(
  storeId: string,
  range: RangeKey,
): StaffSummary {
  const staff = buildStaff(storeId, range);
  const count = (a: Attendance) =>
    staff.filter((s) => s.attendance === a).length;

  return {
    present: count('present'),
    absent: count('absent'),
    late: count('late'),
    onLeave: count('leave'),
    total: staff.length,
  };
}

/** Daily attendance split for the stacked chart. */
export function buildAttendance(
  storeId: string,
  range: RangeKey,
  endMs: number,
): AttendancePoint[] {
  const days = RANGE_DAYS[range];
  const total = buildStaff(storeId, range).length;
  const rng = createRng('attendance', storeId, range);

  return Array.from({length: days}, (_, i) => {
    const date = new Date(endMs - (days - 1 - i) * 86400000);
    const dow = (date.getUTCDay() + 6) % 7;
    // Weekends run leaner rosters, and Mondays carry more late arrivals.
    const rostered = Math.round(total * (dow >= 5 ? 0.72 : 0.94));
    const late = Math.round(rostered * (dow === 0 ? 0.14 : 0.07) * rng.float(0.6, 1.4));
    const absent = Math.round(rostered * 0.06 * rng.float(0.4, 1.6));

    return {
      t: date.toISOString().slice(0, 10),
      present: Math.max(0, rostered - late - absent),
      late,
      absent,
    };
  });
}
