/**
 * The shapes the Learn page reads off the wire, and the one decision every
 * player has to make: iframe or `<video>`.
 *
 * The API is mid-migration. It serialises `embedUrl` today, and a self-hosted
 * provider is landing that resolves to `LearnEmbed` — a discriminated union
 * carrying `kind`. Rather than wait for the shape to settle, this reads
 * whichever of the three forms is present, in order of how much the server has
 * actually told us: an explicit `embed` object, then a `kind` beside the url,
 * then an inference from the url itself. The inference is the only branch that
 * guesses, and it guesses in the safe direction — a relative path is bytes
 * this origin serves, so it becomes a `<video>` and never an iframe pointed at
 * our own origin.
 */
import { LEARN_MEDIA_PATH_PREFIX, type LearnTrack, type LearnVisibility } from '@pig/core';

/**
 * Structural rather than an import of `LearnEmbed`, deliberately. This file
 * describes *untrusted JSON*, not the server's type: a field the server has
 * not sent yet must be optional here or the compiler will assert a guarantee
 * the response does not carry.
 */
export interface LearnEmbedPayload {
  kind?: string;
  src?: string;
  poster?: string;
}

export interface LearnResourceView {
  id: string;
  track: LearnTrack;
  title: string;
  summary: string | null;
  provider: string;
  visibility: LearnVisibility;
  durationSeconds: number | null;
  sortOrder: number;
  publishedAt: string;
  /** The settled shape. Present once the self-hosted provider lands. */
  embed?: LearnEmbedPayload | null;
  /** The discriminator on its own, if it arrives beside the url instead. */
  embedKind?: string | null;
  /** Today's shape: a resolved url with no discriminator. */
  embedUrl?: string | null;
  watchUrl?: string | null;
}

export interface MemberFeed {
  tracks: Record<LearnTrack, LearnResourceView[]>;
  canManage: boolean;
}

export interface PublicFeed {
  track: LearnTrack;
  expiresAt: string;
  resources: LearnResourceView[];
  lockedTracks: LearnTrack[];
}

export type LearnPlayback =
  | { kind: 'iframe'; src: string }
  | { kind: 'video'; src: string; poster?: string };

/**
 * A source with no host is a source on this origin, and this origin serves
 * media files, not embeddable documents. Protocol-relative (`//host/…`) is
 * excluded because it is another origin wearing a relative path's clothes.
 */
function isSelfHostedSource(src: string): boolean {
  if (src.startsWith(LEARN_MEDIA_PATH_PREFIX)) return true;
  return src.startsWith('/') && !src.startsWith('//');
}

export function learnPlayback(resource: LearnResourceView): LearnPlayback | null {
  const embed = resource.embed;
  if (embed?.src) {
    if (embed.kind === 'video') return { kind: 'video', src: embed.src, poster: embed.poster };
    if (embed.kind === 'iframe') return { kind: 'iframe', src: embed.src };
  }

  const src = embed?.src ?? resource.embedUrl;
  if (!src) return null;

  if (resource.embedKind === 'video') return { kind: 'video', src };
  if (resource.embedKind === 'iframe') return { kind: 'iframe', src };

  const selfHosted = resource.provider === 'pig' || isSelfHostedSource(src);
  return selfHosted ? { kind: 'video', src } : { kind: 'iframe', src };
}

/**
 * `sortOrder` is the curriculum's running order and the reason these are a
 * course rather than a pile. Sorted here as well as in the API because the
 * page renders two different feeds and only one of them is guaranteed to have
 * come through the member query's ordering.
 */
export function bySortOrder(resources: readonly LearnResourceView[]): LearnResourceView[] {
  return [...resources].sort(
    (a, b) => a.sortOrder - b.sortOrder || a.publishedAt.localeCompare(b.publishedAt),
  );
}

/** The host of a share link, for a label. Never used to build a src. */
export function watchHost(watchUrl: string | null | undefined): string | null {
  if (!watchUrl) return null;
  try {
    return new URL(watchUrl).hostname;
  } catch {
    return null;
  }
}
