import API from '../../api.js';

// Fields that users can override on an auto-synced channel. Mirrors
// `OVERRIDABLE_FIELDS` in `apps/channels/managers.py`. Keep the two in sync.
export const OVERRIDABLE_FIELDS = [
  'name',
  'channel_number',
  'channel_group_id',
  'logo_id',
  'tvg_id',
  'tvc_guide_stationid',
  'epg_data_id',
  'stream_profile_id',
];

// Display labels for the override fields above.
export const OVERRIDE_FIELD_LABELS = {
  name: 'Name',
  channel_number: 'Channel Number',
  channel_group_id: 'Channel Group',
  logo_id: 'Logo',
  tvg_id: 'TVG-ID',
  tvc_guide_stationid: 'Gracenote Station ID',
  epg_data_id: 'EPG',
  stream_profile_id: 'Stream Profile',
};

export const matchChannelEpg = (channel) => {
  return API.matchChannelEpg(channel.id);
};
export const createLogo = (newLogoData) => {
  return API.createLogo(newLogoData);
};
const setChannelEPG = (channel, values) => {
  return API.setChannelEPG(channel.id, values.epg_data_id);
};
const updateChannel = (values) => {
  return API.updateChannel(values);
};
export const addChannel = (channel) => {
  return API.addChannel(channel);
};
export const requeryChannels = () => {
  API.requeryChannels();
};

// PATCH semantic: `override: null` deletes the override row; per-field
// nulls only clear the matching field.
export const clearChannelOverrides = (channelId) => {
  return API.updateChannel({ id: channelId, override: null });
};

// Coerce a form value to the backend's storage shape so equality
// comparisons against channel data don't drift on type mismatches.
export const normalizeFieldValue = (field, value) => {
  if (value === '' || value === null || value === undefined || value === '-1') {
    return null;
  }
  // The stream_profile and logo pickers encode "(use default)" as '0',
  // which is semantically a null FK on the Channel row.
  if ((field === 'stream_profile_id' || field === 'logo_id') && value === '0') {
    return null;
  }
  if (field === 'channel_number') {
    const n = parseFloat(value);
    return Number.isFinite(n) ? n : null;
  }
  if (
    field === 'channel_group_id' ||
    field === 'logo_id' ||
    field === 'epg_data_id' ||
    field === 'stream_profile_id'
  ) {
    const n = parseInt(value, 10);
    return Number.isFinite(n) ? n : null;
  }
  return value;
};

// Per-field form shape for FK pickers (string for popovers, raw for
// the EPG select); used by the reset-to-provider affordance.
const PROVIDER_FORM_VALUE_BUILDERS = {
  channel_group_id: (channel) =>
    channel?.channel_group_id != null ? `${channel.channel_group_id}` : '',
  stream_profile_id: (channel) =>
    channel?.stream_profile_id != null ? `${channel.stream_profile_id}` : '0',
  logo_id: (channel) => (channel?.logo_id != null ? `${channel.logo_id}` : ''),
  epg_data_id: (channel) => channel?.epg_data_id ?? '',
};

export const getProviderFormValue = (channel, field) => {
  const builder = PROVIDER_FORM_VALUE_BUILDERS[field];
  if (builder) return builder(channel);
  return channel?.[field] ?? '';
};

// Form value differs from the channel's provider value. Manual
// channels always return false (no provider value to compare to).
export const isFormFieldOverridden = (channel, field, formValue) => {
  if (!channel?.auto_created) return false;
  const normalizedForm = normalizeFieldValue(field, formValue);
  const normalizedProvider = normalizeFieldValue(field, channel[field]);
  return normalizedForm !== normalizedProvider;
};

// Human labels for the table's overrides indicator tooltip.
export const listOverriddenFields = (channel) => {
  if (!channel?.override) return [];
  return OVERRIDABLE_FIELDS.filter((field) => {
    const value = channel.override[field];
    return value !== null && value !== undefined;
  }).map((field) => OVERRIDE_FIELD_LABELS[field]);
};

// "Provider: <value>" subtext for auto-synced channels (null for manual).
export const getProviderHint = (channel, field) => {
  if (!channel?.auto_created) return null;
  const providerValue = channel[field];
  const display =
    providerValue === null ||
    providerValue === undefined ||
    providerValue === ''
      ? '(empty)'
      : providerValue;
  return `Provider: ${display}`;
};

// FK provider hint that resolves the ID to a display name via lookup.
export const getFkProviderHint = (channel, field, lookup) => {
  if (!channel?.auto_created) return null;
  const providerId = channel[field];
  if (providerId === null || providerId === undefined) {
    return 'Provider: (none)';
  }
  const entry = lookup?.[providerId];
  const display = entry?.name || entry?.tvg_id || String(providerId);
  return `Provider: ${display}`;
};

// Build the override PATCH payload by diffing form values against
// provider values. Matching fields become null (clear); diverging
// fields carry the form value.
export const buildOverridePayload = (channel, formattedValues) => {
  if (!channel) return undefined;
  const payload = {};
  let anyOverride = false;

  for (const field of OVERRIDABLE_FIELDS) {
    const formValue = normalizeFieldValue(field, formattedValues[field]);
    const providerValue = normalizeFieldValue(field, channel[field]);
    if (formValue === null && providerValue === null) continue;
    if (formValue !== providerValue) {
      payload[field] = formValue;
      if (formValue !== null) anyOverride = true;
    } else {
      payload[field] = null;
    }
  }

  if (!anyOverride) {
    // Every field matches provider; explicit null deletes the row.
    return null;
  }
  return payload;
};

// Prefer the backend-resolved effective_* so the form loads with the
// overridden value; fall back to the raw field otherwise.
const effective = (channel, field) => {
  if (!channel) return undefined;
  const effKey = `effective_${field}`;
  if (effKey in channel && channel[effKey] !== undefined) {
    return channel[effKey];
  }
  return channel[field];
};

export const getChannelFormDefaultValues = (channel, channelGroups) => {
  const name = effective(channel, 'name') ?? '';
  const channelNumber = effective(channel, 'channel_number');
  const groupId = effective(channel, 'channel_group_id');
  const streamProfileId = effective(channel, 'stream_profile_id');
  const tvgId = effective(channel, 'tvg_id');
  const gracenoteId = effective(channel, 'tvc_guide_stationid');
  const epgDataId = effective(channel, 'epg_data_id');
  const logoId = effective(channel, 'logo_id');
  return {
    name: name || '',
    channel_number:
      channelNumber !== null && channelNumber !== undefined
        ? channelNumber
        : '',
    channel_group_id: groupId
      ? `${groupId}`
      : Object.keys(channelGroups).length > 0
        ? Object.keys(channelGroups)[0]
        : '',
    stream_profile_id: streamProfileId ? `${streamProfileId}` : '0',
    tvg_id: tvgId || '',
    tvc_guide_stationid: gracenoteId || '',
    epg_data_id: epgDataId ?? '',
    logo_id: logoId ? `${logoId}` : '',
    user_level: `${channel?.user_level ?? '0'}`,
    is_adult: channel?.is_adult ?? false,
    hidden_from_output: channel?.hidden_from_output ?? false,
  };
};

export const getFormattedValues = (values) => {
  const formattedValues = { ...values };

  // Convert empty or "0" stream_profile_id to null for the API
  if (
    !formattedValues.stream_profile_id ||
    formattedValues.stream_profile_id === '0'
  ) {
    formattedValues.stream_profile_id = null;
  }

  // Ensure tvg_id is properly included (no empty strings)
  formattedValues.tvg_id = formattedValues.tvg_id || null;

  // Ensure tvc_guide_stationid is properly included (no empty strings)
  formattedValues.tvc_guide_stationid =
    formattedValues.tvc_guide_stationid || null;

  return formattedValues;
};

export const handleEpgUpdate = async (
  channel,
  values,
  formattedValues,
  channelStreams
) => {
  // Auto-synced channels route identity edits into the override row. Sync
  // keeps writing provider values to Channel.* unmodified, so the override
  // is what actually persists user changes across refreshes. `hidden_from_output`
  // stays as a direct Channel field even for auto-created channels because
  // it is a status flag, not a value replacement.
  if (channel.auto_created) {
    const overridePayload = buildOverridePayload(channel, formattedValues);
    const payload = {
      id: channel.id,
      hidden_from_output: formattedValues.hidden_from_output,
    };
    if (overridePayload !== undefined) {
      payload.override = overridePayload;
    }
    await updateChannel(payload);
    return;
  }

  // Manual channels: existing behavior preserved. When the EPG has changed,
  // the dedicated set-EPG endpoint triggers an EPG refresh; other field
  // updates go through the regular PATCH and are skipped entirely when
  // there is nothing besides epg_data_id to update.
  if (values.epg_data_id !== (channel.epg_data_id ?? '')) {
    await setChannelEPG(channel, values);

    const { epg_data_id: _epg_data_id, ...otherValues } = formattedValues;
    if (Object.keys(otherValues).length > 0) {
      await updateChannel({
        id: channel.id,
        ...otherValues,
        streams: channelStreams.map((stream) => stream.id),
      });
    }
  } else {
    await updateChannel({
      id: channel.id,
      ...formattedValues,
      streams: channelStreams.map((stream) => stream.id),
    });
  }
};
