import type { HostDetailEntry, HostsState, VitalsEntry } from './index';

// Any state with the `hosts` slice mounted at `state.hosts` — works for the
// hosts-app store but also for any consumer that composes this reducer.
export interface WithHostsSlice {
  hosts: HostsState;
}

const selectHosts = (state: WithHostsSlice) => state.hosts;

// Frozen empty objects with stable identity — returning the same reference on
// every "no entry" lookup keeps `useSelector` equality checks happy and avoids
// re-renders that would otherwise fire because of a new {} per call.
const EMPTY_DETAIL: HostDetailEntry = Object.freeze({
  info: null,
  infoStatus: 'idle' as const,
  infoError: null,
  sharedGroups: null,
  statusMeasured: null,
  softwareSummary: null,
  policyExecution: null,
  pinnedItems: null
});

const EMPTY_VITALS: VitalsEntry = Object.freeze({
  meta: null,
  metaStatus: 'idle' as const,
  series: Object.freeze({}) as Record<string, never>
});

// Selectors that read a slice by hostkey are plain functions (not
// createSelector). Two reasons:
//  1. The read is a single Record lookup + nullish coalesce — memoization
//     buys nothing.
//  2. The previous `(hostkey) => createSelector(...)` factory pattern built
//     a brand-new memoized selector every render, defeating the cache
//     entirely. Plain readers are equivalent in correctness and faster.
//
// Stable empty fallbacks (EMPTY_DETAIL / EMPTY_VITALS) preserve referential
// identity across calls so React.useSelector skips re-renders when the entry
// is missing.

export const selectHostDetail = (state: WithHostsSlice, hostkey: string | undefined) =>
  hostkey ? state.hosts.detail[hostkey] ?? EMPTY_DETAIL : EMPTY_DETAIL;

export const selectHostInfo = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).info;

export const selectHostInfoStatus = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).infoStatus;

export const selectHostInfoError = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).infoError;

export const selectSharedGroups = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).sharedGroups;

export const selectStatusMeasured = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).statusMeasured;

export const selectSoftwareSummary = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).softwareSummary;

export const selectPolicyExecution = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).policyExecution;

export const selectPinnedItems = (state: WithHostsSlice, hostkey: string | undefined) =>
  selectHostDetail(state, hostkey).pinnedItems;

export const selectVitals = (state: WithHostsSlice, hostkey: string | undefined) =>
  hostkey ? state.hosts.vitals[hostkey] ?? EMPTY_VITALS : EMPTY_VITALS;

export const selectVitalSeries = (
  state: WithHostsSlice,
  hostkey: string | undefined,
  obs: string,
  range: 'past4h' | 'pastWeek' | 'pastYear'
) => selectVitals(state, hostkey).series[`${obs}:${range}`];

// Top-level slice readers are plain functions: each just returns a single
// stored value, so createSelector's memoization would buy nothing (the value
// already has stable identity until the slice changes it).
export const selectHostList = (state: WithHostsSlice) => selectHosts(state).hostList;
export const selectHostListLoading = (state: WithHostsSlice) =>
  selectHosts(state).loadingStates.hostList;
export const selectHostListError = (state: WithHostsSlice) => selectHosts(state).hostListError;

export const selectCompliance = (state: WithHostsSlice) => selectHosts(state).compliance;
export const selectComplianceLoading = (state: WithHostsSlice) =>
  selectHosts(state).loadingStates.compliance;
export const selectComplianceError = (state: WithHostsSlice) => selectHosts(state).complianceError;

export const selectHostsLoadingStates = (state: WithHostsSlice) => selectHosts(state).loadingStates;
