// Shared "advanced channel options" model — the per-field override semantics over
// a channel template's baseline, used by BOTH the manual Channel Builder
// (ChannelBuilder.tsx) and the Auto-Tune per-channel DetailPanel (AutoTuneScreen.tsx).
//
// The tricky, bug-prone part is the INHERIT/omit vs CLEAR contract (see #135). Three states
// per advanced field:
//   - INHERIT: the field is OMITTED from the request; the create handler coalesces the missing
//     value with the template's value (the historical default for every existing client).
//   - a set value: sent as-is, overriding the template.
//   - CLEAR ("None"): the field is added to `advanced.clear`, forcing it to none on the new
//     channel even when the template sets one. Only the id fields (watermark + the four fillers)
//     expose a "None" option in the SPA today; the backend `clear` enum also covers the preferred
//     audio/subtitle language strings for machine clients (API-ahead-of-UI — see spa-conventions).
// Both consumers MUST honour that, so it lives here once. Consumers render their own field JSX;
// only the model + adapters are shared.

import type { ChannelTemplate, CreateChannelFromLineupClearField } from '../api';

// ---- Enum unions (hand-listed from generated v1.d.ts; keep in sync) --------
export const PLAYBACK_ORDERS = [
  'None',
  'Chronological',
  'Random',
  'Shuffle',
  'ShuffleInOrder',
  'MultiEpisodeShuffle',
  'SeasonEpisode',
  'RandomRotation',
  'Marathon'
] as const;
export type PlaybackOrder = (typeof PLAYBACK_ORDERS)[number];

export const STREAMING_MODES = [
  'TransportStream',
  'HttpLiveStreamingDirect',
  'HttpLiveStreamingSegmenter',
  'TransportStreamHybrid'
] as const;
export type StreamingMode = (typeof STREAMING_MODES)[number];

export const SUBTITLE_MODES = ['None', 'Forced', 'Default', 'Any'] as const;
export type SubtitleMode = (typeof SUBTITLE_MODES)[number];

export const STREAM_SELECTOR_MODES = ['Default', 'Custom', 'Troubleshooting'] as const;
export type StreamSelectorMode = (typeof STREAM_SELECTOR_MODES)[number];

export const MUSIC_VIDEO_CREDITS_MODES = ['None', 'GenerateSubtitles'] as const;
export type MusicVideoCreditsMode = (typeof MUSIC_VIDEO_CREDITS_MODES)[number];

export const SONG_VIDEO_MODES = ['Default', 'WithProgress'] as const;
export type SongVideoMode = (typeof SONG_VIDEO_MODES)[number];

export const IDLE_BEHAVIORS = ['StopOnDisconnect', 'KeepRunning'] as const;
export type IdleBehavior = (typeof IDLE_BEHAVIORS)[number];

export const FIXED_START_TIME_BEHAVIORS = ['Strict', 'Flexible'] as const;
export type FixedStartTimeBehavior = (typeof FIXED_START_TIME_BEHAVIORS)[number];

export const TRANSCODE_MODES = ['OnDemand'] as const;
export type TranscodeMode = (typeof TRANSCODE_MODES)[number];

export type PlayoutMode = 'Continuous' | 'OnDemand';

// Streaming mode friendly labels for chips / selects.
export const STREAMING_MODE_LABELS: Record<StreamingMode, string> = {
  TransportStream: 'MPEG-TS',
  HttpLiveStreamingDirect: 'HLS Direct',
  HttpLiveStreamingSegmenter: 'HLS Segmenter',
  TransportStreamHybrid: 'MPEG-TS Hybrid'
};

// Advanced field keys wired into request.advanced. playbackOrder + playoutMode are
// deliberately EXCLUDED: the Channel Builder keeps them in dedicated state (always
// sent), and the Auto-Tune DetailPanel surfaces them as the two prominent Playback
// toggles — both write them separately rather than through this override map.
export const ADVANCED_KEYS = [
  'ffmpegProfileId',
  'watermarkId',
  'fallbackFillerId',
  'preRollFillerId',
  'midRollFillerId',
  'postRollFillerId',
  'streamSelectorMode',
  'streamSelector',
  'preferredAudioLanguageCode',
  'preferredAudioTitle',
  'streamingMode',
  'preferredSubtitleLanguageCode',
  'subtitleMode',
  'musicVideoCreditsMode',
  'songVideoMode',
  'transcodeMode',
  'idleBehavior',
  'randomStartPoint',
  'shuffleScheduleItems',
  'fixedStartTimeBehavior'
] as const;
export type AdvancedKey = (typeof ADVANCED_KEYS)[number];

export type Overrides = Partial<Record<AdvancedKey, unknown>>;

// Sentinel select value meaning "inherit from the template" — the field is
// omitted from `advanced` entirely (not sent as null). See #135.
export const INHERIT = '__inherit__';

// Sentinel override value meaning "clear to none" — the field is added to the request's
// `advanced.clear` list, forcing it to none even when the template sets one (#135). Stored in the
// overrides map (a clear IS a divergence from the template) and translated at request-build time.
export const CLEAR = '__clear__';

// AdvancedKeys whose "None" maps to a backend clear-field. Only these can be cleared; every other
// key set to CLEAR is ignored by collectClears. The id keys drive the SPA "None" option today; the
// string keys are covered so a machine client (MCP) can clear them via the same enum.
export const CLEARABLE_FIELDS: Partial<Record<AdvancedKey, CreateChannelFromLineupClearField>> = {
  watermarkId: 'Watermark',
  fallbackFillerId: 'FallbackFiller',
  preRollFillerId: 'PreRollFiller',
  midRollFillerId: 'MidRollFiller',
  postRollFillerId: 'PostRollFiller',
  preferredAudioLanguageCode: 'PreferredAudioLanguage',
  preferredAudioTitle: 'PreferredAudioTitle',
  preferredSubtitleLanguageCode: 'PreferredSubtitleLanguage'
};

// The current effective value for an advanced field = user override (if any) else the selected
// template's value. A CLEAR override reads as none (null), so pickers/previews render "None".
export function effectiveValue(key: AdvancedKey, template: ChannelTemplate, overrides: Overrides): unknown {
  if (key in overrides) {
    return overrides[key] === CLEAR ? null : overrides[key];
  }
  return (template as unknown as Record<string, unknown>)[key];
}

// The backend clear-field enum values for every key set to CLEAR in the overrides map.
export function collectClears(overrides: Overrides): CreateChannelFromLineupClearField[] {
  const clears: CreateChannelFromLineupClearField[] = [];
  for (const key of ADVANCED_KEYS) {
    if (overrides[key] === CLEAR) {
      const field = CLEARABLE_FIELDS[key];
      if (field) {
        clears.push(field);
      }
    }
  }
  return clears;
}

// Fold an overrides map into an `advanced` request object: copy every set (non-CLEAR) override, then
// attach `clear` for any CLEAR-valued key. Shared by both consumers so the CLEAR sentinel never leaks
// onto the wire as a field value. `base` carries consumer-specific extras (playbackOrder/playoutMode).
export function applyOverridesToRequest<T extends Record<string, unknown>>(base: T, overrides: Overrides): T {
  for (const key of ADVANCED_KEYS) {
    if (!(key in overrides)) {
      continue;
    }
    // A CLEAR sentinel is folded into `clear` below, but only for clearable fields. Any other key
    // holding the sentinel is only reachable if a user literally typed it into a text input — copy
    // it through as its value rather than silently dropping the field from the request.
    if (overrides[key] === CLEAR && key in CLEARABLE_FIELDS) {
      continue;
    }
    (base as Record<string, unknown>)[key] = overrides[key];
  }
  const clear = collectClears(overrides);
  if (clear.length > 0) {
    (base as Record<string, unknown>).clear = clear;
  }
  return base;
}

// Templates carry no playbackOrder; the Shuffle toggle *is* the template's
// declared shuffle intent (template.shuffleScheduleItems -> Shuffle/Chronological).
export function templatePlaybackOrder(template: ChannelTemplate): PlaybackOrder {
  return template.shuffleScheduleItems ? 'Shuffle' : 'Chronological';
}

export function fillerName(rows: Array<{ id: number; name: string | null }>, id: number | null): string {
  if (id == null) {
    return 'None';
  }
  const row = rows.find((candidate) => candidate.id === id);
  return row?.name ?? `#${id}`;
}

export function inheritOption(inheritedLabel: string | null): { value: string; label: string } {
  return {
    value: INHERIT,
    label: inheritedLabel ? `Inherit (${inheritedLabel})` : 'Inherit from template'
  };
}

// Field adapters bound to one (template, overrides, setOverrides) triple. This is the
// shared INHERIT/omit logic: a select set back to INHERIT or a text input cleared to ''
// REMOVES the override (field omitted from the request) rather than sending null/''.
export function useAdvancedOverrides(
  template: ChannelTemplate,
  overrides: Overrides,
  setOverrides: (updater: (prev: Overrides) => Overrides) => void
) {
  const setOverride = (key: AdvancedKey, value: unknown) => setOverrides((prev) => ({ ...prev, [key]: value }));
  const removeOverride = (key: AdvancedKey) =>
    setOverrides((prev) => {
      const next = { ...prev };
      delete next[key];
      return next;
    });

  const templateValueOf = (key: AdvancedKey): unknown => (template as unknown as Record<string, unknown>)[key];

  // Override selects: the first option is always "Inherit from template" (the INHERIT sentinel ->
  // field omitted from the request), followed by an explicit "None" (the CLEAR sentinel -> field
  // added to advanced.clear, forcing it to none even when the template sets one) for clearable
  // fields, then the concrete rows. "None" is distinct from "Inherit": inherit keeps the template
  // value, None drops it (#135).
  const idSelectOptions = (key: AdvancedKey, rows: Array<{ id: number; name: string | null }>) => {
    const inherited = templateValueOf(key) as number | null;
    return [
      inheritOption(inherited == null ? null : fillerName(rows, inherited)),
      ...(key in CLEARABLE_FIELDS ? [{ value: CLEAR, label: 'None' }] : []),
      ...rows.map((row) => ({ value: String(row.id), label: row.name ?? `#${row.id}` }))
    ];
  };

  // Selected value for an override select: the override when present (CLEAR renders as the "None"
  // option since String(CLEAR) === CLEAR), else INHERIT.
  const selValue = (key: AdvancedKey): string => (key in overrides ? String(overrides[key]) : INHERIT);
  const onSelect =
    (key: AdvancedKey, map: (raw: string) => unknown) => (event: React.ChangeEvent<HTMLSelectElement>) => {
      const raw = event.target.value;
      if (raw === INHERIT) {
        removeOverride(key);
      } else if (raw === CLEAR) {
        setOverride(key, CLEAR);
      } else {
        setOverride(key, map(raw));
      }
    };

  // Text inputs: empty string = inherit (field omitted); never send null or ''.
  const textValue = (key: AdvancedKey): string => (key in overrides ? String(overrides[key] ?? '') : '');
  const onText = (key: AdvancedKey) => (event: React.ChangeEvent<HTMLInputElement>) => {
    const raw = event.target.value;
    if (raw === '') {
      removeOverride(key);
    } else {
      setOverride(key, raw);
    }
  };
  const textPlaceholder = (key: AdvancedKey, fallback?: string): string | undefined => {
    const inherited = templateValueOf(key);
    return inherited ? `Inherit: ${String(inherited)}` : fallback;
  };

  const boolValue = (key: AdvancedKey): boolean => Boolean(effectiveValue(key, template, overrides));

  return {
    setOverride,
    removeOverride,
    templateValueOf,
    idSelectOptions,
    selValue,
    onSelect,
    textValue,
    onText,
    textPlaceholder,
    boolValue
  };
}
