/**
 * The wire format.
 *
 * Imported by BOTH the route handlers and the client hooks, so any drift
 * between server and client is a type error rather than a runtime surprise.
 * When a real backend arrives this file is the negotiation artifact — it is
 * already the contract, not a description of one.
 */

export type RangeKey = '7d' | '30d' | '90d' | 'mtd' | 'ytd' | 'custom';

export interface ApiMeta {
  generatedAt: string;
  range: RangeKey;
  storeId: string;
}

export interface ApiSuccess<T> {
  data: T;
  meta: ApiMeta;
}

export type ApiErrorCode =
  | 'internal'
  | 'not_found'
  | 'bad_request'
  | 'unauthorized';

export interface ApiFailure {
  error: {code: ApiErrorCode; message: string};
}

export type ApiResponse<T> = ApiSuccess<T> | ApiFailure;

export function isFailure<T>(r: ApiResponse<T>): r is ApiFailure {
  return (r as ApiFailure).error !== undefined;
}

// ---------------------------------------------------------------------------
// Domain
// ---------------------------------------------------------------------------

export type KpiId = 'visitors' | 'purchases' | 'revenue' | 'activeRewards';

/** `unit` drives formatting at the edge; the value stays a plain number. */
export type KpiUnit = 'count' | 'inr' | 'lyt' | 'pct';

export interface Kpi {
  id: KpiId;
  label: string;
  value: number;
  unit: KpiUnit;
  /** Change against the previous comparable period. */
  deltaPct: number;
  /** Whether a rise is good. Churn rises are not. */
  isRiseGood: boolean;
  trend: {t: string; v: number}[];
}

export interface TimePoint {
  t: string;
  visitors: number;
  purchases: number;
  revenue: number;
  /** Purchases ÷ visitors, as a percentage. */
  conversion: number;
}

export interface HourCell {
  day: number;
  hour: number;
  value: number;
}

export type StoreStatus = 'open' | 'closed' | 'maintenance';

export interface Store {
  id: string;
  name: string;
  status: StoreStatus;
  visitors: number;
  purchases: number;
  revenueInr: number;
  conversionPct: number;
  staffCount: number;
}

export type RewardStatus = 'active' | 'paused' | 'expiring' | 'expired';

export interface Reward {
  id: string;
  name: string;
  costLyt: number;
  claimed: number;
  used: number;
  expiresAt: string | null;
  status: RewardStatus;
}

export type Attendance = 'present' | 'absent' | 'late' | 'leave';

export interface StaffMember {
  id: string;
  name: string;
  avatarUrl: string | null;
  /** Which store they are rostered at. */
  storeId: string;
  role: string;
  attendance: Attendance;
  salesCount: number;
  rewardsIssued: number;
  /** 0–100. */
  performanceScore: number;
  /** Share of shifts attended on time, over the selected period. */
  punctualityPct: number;
}

export type ActivityKind =
  | 'reward_redeemed'
  | 'staff_checked_in'
  | 'purchase'
  | 'reward_expired'
  | 'store_opened';

export interface ActivityEvent {
  id: string;
  at: string;
  kind: ActivityKind;
  title: string;
  detail?: string;
  storeId: string;
}

/** One store's totals for the comparison small-multiples. */
export interface StoreComparison {
  storeId: string;
  name: string;
  visitors: number;
  purchases: number;
  revenueInr: number;
  conversionPct: number;
  /** 14-point trend for the store's sparkline. */
  trend: {t: string; v: number}[];
}

/** Claim-vs-use for a single reward, driving the Reward Usage chart. */
export interface RewardUsagePoint {
  rewardId: string;
  name: string;
  claimed: number;
  used: number;
  /** used ÷ claimed, as a percentage. */
  usageRatePct: number;
}

/** Bucketed period totals behind the Weekly / Monthly toggle. */
export type Granularity = 'weekly' | 'monthly';

export interface PeriodPoint {
  /** Bucket label — "W32" or "Aug". */
  label: string;
  visitors: number;
  purchases: number;
  revenue: number;
}

/** Headline attendance counts for the Staff module. */
export interface StaffSummary {
  present: number;
  absent: number;
  late: number;
  onLeave: number;
  total: number;
}

/** One day's attendance split, for the stacked attendance chart. */
export interface AttendancePoint {
  t: string;
  present: number;
  late: number;
  absent: number;
}

/** A member's sales contribution over the selected period. */
export interface StaffSalesPoint {
  t: string;
  sales: number;
}

/** Merchant account settings. */
export interface MerchantProfile {
  businessName: string;
  contactName: string;
  email: string;
  phone: string;
  gstin: string;
  /** IANA zone — all reporting timestamps are rendered in it. */
  timezone: string;
  currency: 'INR';
  /** Default LYTs granted per ₹100 spent. */
  lytsPerHundred: number;
  notifications: {
    expiryAlerts: boolean;
    dailySummary: boolean;
    staffAbsence: boolean;
    lowConversion: boolean;
  };
}

export type InsightSeverity = 'info' | 'success' | 'warning' | 'error';

export interface Insight {
  id: string;
  severity: InsightSeverity;
  title: string;
  body: string;
  action?: {label: string; href: string};
}

/** One item on the day's operational checklist. */
export interface DashboardTask {
  id: string;
  label: string;
  detail?: string;
  /** Plain-language deadline — "before 18:00", "today". Never a raw date. */
  due: string;
  isDone: boolean;
  action?: {label: string; href: string};
}

/**
 * The operational half of the dashboard, in one payload.
 *
 * Summary, alerts and tasks ship together because they are three renderings of
 * the same reasoning pass: the narrative states what happened, the alerts say
 * which parts of it need a decision, and the tasks are what that decision
 * costs. Splitting them across three endpoints would let a merchant read a
 * summary generated from one snapshot beside alerts generated from another.
 */
export interface DashboardBriefing {
  /** One-paragraph narrative of the selected scope and period. */
  summary: string;
  /** Most severe first. */
  alerts: Insight[];
  tasks: DashboardTask[];
}
