import api from './api';

// These types intentionally mirror the durable session API. Tracks are not
// embedded in a plan revision: the client resolves ids through the normal
// library endpoint so a deleted/hidden track can never become playable merely
// because an older plan mentioned it.
export interface VibePlanItem {
  plan_version_id: string;
  ordinal: number;
  track_id: string;
  slot_role: string | null;
  candidate_source: string;
  score: number;
  score_breakdown: Record<string, unknown>;
  explanation: unknown;
  committed: boolean;
}

export interface DurableVibeSessionResponse {
  sessionId: string;
  /** The durable session row. Present on start, resume and plan reads. */
  session?: { id: string; seed_track_id: string | null };
  planVersion: number | null;
  now: VibePlanItem | null;
  preview: VibePlanItem[];
  state: Record<string, unknown>;
  replanned: boolean;
  replanReason: string | null;
}

export type VibeEventType =
  | 'playback_started'
  | 'progress'
  | 'completed'
  | 'skipped'
  | 'disliked'
  | 'kept';

export interface VibeEventInput {
  eventId: string;
  type: VibeEventType;
  trackId?: string;
  occurredAt: string;
  positionMs?: number;
  durationMs?: number;
  payload?: Record<string, unknown>;
}

export interface VibeEventResponse extends DurableVibeSessionResponse {
  event: { id: string; client_event_id: string | null; type: string };
  idempotent: boolean;
}

/** Coarse local calendar context, used only for short-lived Vibe preferences. */
export interface VibeCalendarContext {
  localHour: number;
  weekday: number;
  month: number;
  timeZone?: string;
}

/** A durable, idempotent advancement past a plan item the player cannot load. */
export interface VibeUnplayableItemInput {
  eventId: string;
  planVersionId: string;
  ordinal: number;
  trackId: string;
}

export const vibeService = {
  async start(seedTrackId?: string, context?: VibeCalendarContext): Promise<DurableVibeSessionResponse> {
    const res = await api.post<DurableVibeSessionResponse>('/v2/vibe/sessions', { seedTrackId, context });
    return res.data;
  },

  /**
   * Pick up a session that is already running, on a device that did not start
   * it. Resuming is how Vibe control follows the audio between devices; it also
   * revives a session the reaper had paused.
   */
  async resume(sessionId: string): Promise<DurableVibeSessionResponse> {
    const res = await api.post<DurableVibeSessionResponse>('/v2/vibe/sessions', { resumeSessionId: sessionId });
    return res.data;
  },

  async getPlan(sessionId: string, version?: number): Promise<DurableVibeSessionResponse> {
    const res = await api.get<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/plans`, {
      params: version === undefined ? undefined : { version },
    });
    return res.data;
  },

  async next(sessionId: string, expectedPlanVersion: number): Promise<DurableVibeSessionResponse> {
    const res = await api.post<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/advance`, {
      expectedPlanVersion,
    });
    return res.data;
  },

  async advancePastUnplayable(
    sessionId: string,
    expectedPlanVersion: number,
    unplayable: VibeUnplayableItemInput,
  ): Promise<DurableVibeSessionResponse> {
    const res = await api.post<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/advance`, {
      expectedPlanVersion,
      unplayable,
    });
    return res.data;
  },

  async event(sessionId: string, event: VibeEventInput): Promise<VibeEventResponse> {
    const res = await api.post<VibeEventResponse>(`/v2/vibe/sessions/${sessionId}/events`, event);
    return res.data;
  },

  async end(sessionId: string): Promise<DurableVibeSessionResponse> {
    const res = await api.post<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/end`);
    return res.data;
  },
};
