import { openDB, type DBSchema, type IDBPDatabase } from 'idb';
import { defaultSelectedRadioCountryCodes, managedCountryCode } from '../radio/radioCountries.js';

export type PersistedStationHealth = {
  attempts: number;
  successes: number;
  failures: number;
  lastAttemptedAt: string | null;
  lastSucceededAt: string | null;
  lastFailedAt: string | null;
  lastFailureReason: string | null;
  timeoutCount: number;
  lastProbeMillis: number | null;
};

export type PersistedRecentStationEntry = {
  stationId: string;
  playedAt: string;
  country: string | null;
};

export type PersistedSongHistoryEntry = {
  artist: string;
  title: string;
  seenAt: string;
};

type PersistedSettings = {
  volume: number | null;
  lastStationId: string | null;
  lastStationCountry: string | null;
  selectedRadioCountryCodes: string[];
  castBothMode: boolean;
  favoriteStationIds: string[];
  stationUsageCounts: Record<string, number>;
  recentStationHistory: PersistedRecentStationEntry[];
  stationSongHistory: Record<string, PersistedSongHistoryEntry[]>;
  stationHealth: Record<string, PersistedStationHealth>;
  lastExportedAt: string | null;
  lastImportedAt: string | null;
};

type PersistedSnapshot = PersistedSettings & {
  userStations: unknown[];
};

type SettingKey = keyof PersistedSettings;

type SettingRecordMap = {
  volume: number | null;
  lastStationId: string | null;
  lastStationCountry: string | null;
  selectedRadioCountryCodes: string[];
  castBothMode: boolean;
  favoriteStationIds: string[];
  stationUsageCounts: Record<string, number>;
  recentStationHistory: PersistedRecentStationEntry[];
  stationSongHistory: Record<string, PersistedSongHistoryEntry[]>;
  stationHealth: Record<string, PersistedStationHealth>;
  lastExportedAt: string | null;
  lastImportedAt: string | null;
};

type UserStationRecord = {
  id: string;
  sortOrder: number;
  data: unknown;
};

interface RadioPlayerDb extends DBSchema {
  settings: {
    key: SettingKey;
    value: SettingRecordMap[SettingKey];
  };
  userStations: {
    key: string;
    value: UserStationRecord;
    indexes: {
      'by-sort-order': number;
    };
  };
}

export type PlayerPersistenceSnapshot = PersistedSnapshot;
export type PlayerPersistenceBackend = 'indexeddb' | 'localstorage';

const DB_NAME = 'radioplayer';
const DB_VERSION = 1;

const DEFAULT_SNAPSHOT: PersistedSnapshot = {
  volume: null,
  lastStationId: null,
  lastStationCountry: null,
  selectedRadioCountryCodes: [...defaultSelectedRadioCountryCodes],
  castBothMode: false,
  favoriteStationIds: [],
  stationUsageCounts: {},
  recentStationHistory: [],
  stationSongHistory: {},
  stationHealth: {},
  lastExportedAt: null,
  lastImportedAt: null,
  userStations: [],
};

const LOCAL_STORAGE_KEYS = {
  volume: 'volume',
  lastStationId: 'lastStationId',
  lastStationCountry: 'lastStationCountry',
  selectedRadioCountryCodes: 'selectedRadioCountryCodes',
  castBothMode: 'castBothMode',
  favoriteStationIds: 'favoriteStationIds',
  stationUsageCounts: 'stationUsageCounts',
  recentStationHistory: 'recentStationHistory',
  stationSongHistory: 'stationSongHistory',
  stationHealth: 'stationHealth',
  lastExportedAt: 'lastExportedAt',
  lastImportedAt: 'lastImportedAt',
  userStations: 'userStations',
};

let dbPromise: Promise<IDBPDatabase<RadioPlayerDb> | null> | null = null;
let snapshot: PersistedSnapshot = { ...DEFAULT_SNAPSHOT };
let hydrated = false;
let persistenceBackend: PlayerPersistenceBackend = 'indexeddb';

function getDb() {
  if (!dbPromise) {
    dbPromise = openDB<RadioPlayerDb>(DB_NAME, DB_VERSION, {
      upgrade(db: IDBPDatabase<RadioPlayerDb>) {
        if (!db.objectStoreNames.contains('settings')) {
          db.createObjectStore('settings');
        }

        if (!db.objectStoreNames.contains('userStations')) {
          const store = db.createObjectStore('userStations', { keyPath: 'id' });
          store.createIndex('by-sort-order', 'sortOrder');
        }
      },
    }).catch((error) => {
      persistenceBackend = 'localstorage';
      console.warn('IndexedDB unavailable, falling back to localStorage persistence.', error);
      return null;
    });
  }

  return dbPromise;
}

function safeJsonParse<T>(value: string | null, fallback: T): T {
  if (!value) return fallback;

  try {
    return JSON.parse(value) as T;
  } catch {
    return fallback;
  }
}

function sanitizeSelectedRadioCountryCodes(value: unknown): string[] {
  const codes = Array.isArray(value)
    ? value
        .map((entry) => (typeof entry === 'string' ? entry.trim().toUpperCase() : ''))
        .filter((entry) => /^[A-Z]{2}$/.test(entry))
    : [];

  const uniqueCodes = Array.from(new Set(codes));
  if (!uniqueCodes.includes(managedCountryCode)) {
    uniqueCodes.unshift(managedCountryCode);
  }

  return uniqueCodes.length > 0 ? uniqueCodes : [...defaultSelectedRadioCountryCodes];
}

function sanitizeIsoDate(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  const trimmed = value.trim();
  if (!trimmed) return null;
  const parsed = Date.parse(trimmed);
  return Number.isFinite(parsed) ? new Date(parsed).toISOString() : null;
}

function sanitizeStationHealthRecord(value: unknown): PersistedStationHealth | null {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null;
  }

  const input = value as Record<string, unknown>;
  const attempts = Math.max(0, Number(input.attempts) || 0);
  const successes = Math.max(0, Number(input.successes) || 0);
  const failures = Math.max(0, Number(input.failures) || 0);
  const timeoutCount = Math.max(0, Number(input.timeoutCount) || 0);
  const rawProbeMillis = Number(input.lastProbeMillis);

  return {
    attempts,
    successes,
    failures,
    lastAttemptedAt: sanitizeIsoDate(input.lastAttemptedAt),
    lastSucceededAt: sanitizeIsoDate(input.lastSucceededAt),
    lastFailedAt: sanitizeIsoDate(input.lastFailedAt),
    lastFailureReason: typeof input.lastFailureReason === 'string' && input.lastFailureReason.trim().length > 0
      ? input.lastFailureReason.trim()
      : null,
    timeoutCount,
    lastProbeMillis: Number.isFinite(rawProbeMillis) && rawProbeMillis >= 0 ? rawProbeMillis : null,
  };
}

function sanitizeStationHealthMap(value: unknown): Record<string, PersistedStationHealth> {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return {};
  }

  return Object.fromEntries(
    Object.entries(value as Record<string, unknown>)
      .map(([stationId, record]) => [stationId, sanitizeStationHealthRecord(record)] as const)
      .filter((entry): entry is [string, PersistedStationHealth] => Boolean(entry[0]) && Boolean(entry[1])),
  );
}

function sanitizeRecentStationHistory(value: unknown): PersistedRecentStationEntry[] {
  if (!Array.isArray(value)) {
    return [];
  }

  return value
    .map((entry) => {
      if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
        return null;
      }

      const input = entry as Record<string, unknown>;
      const stationId = typeof input.stationId === 'string' && input.stationId.trim().length > 0
        ? input.stationId.trim()
        : null;
      const playedAt = sanitizeIsoDate(input.playedAt);
      const country = typeof input.country === 'string' && input.country.trim().length > 0
        ? input.country.trim()
        : null;

      if (!stationId || !playedAt) {
        return null;
      }

      return { stationId, playedAt, country };
    })
    .filter((entry): entry is PersistedRecentStationEntry => Boolean(entry));
}

function sanitizeSongHistoryEntry(value: unknown): PersistedSongHistoryEntry | null {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null;
  }

  const input = value as Record<string, unknown>;
  const artist = typeof input.artist === 'string' ? input.artist.trim() : '';
  const title = typeof input.title === 'string' ? input.title.trim() : '';
  const seenAt = sanitizeIsoDate(input.seenAt);

  if (!artist || !title || !seenAt) {
    return null;
  }

  return { artist, title, seenAt };
}

function sanitizeStationSongHistoryMap(value: unknown): Record<string, PersistedSongHistoryEntry[]> {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return {};
  }

  return Object.fromEntries(
    Object.entries(value as Record<string, unknown>)
      .map(([stationId, entries]) => {
        const history = Array.isArray(entries)
          ? entries
              .map((entry) => sanitizeSongHistoryEntry(entry))
              .filter((entry): entry is PersistedSongHistoryEntry => Boolean(entry))
          : [];

        return [stationId, history] as const;
      })
      .filter(([stationId, history]) => Boolean(stationId) && history.length > 0),
  );
}

function cloneStationHealthMap(map: Record<string, PersistedStationHealth>) {
  return Object.fromEntries(
    Object.entries(map).map(([stationId, record]) => [stationId, { ...record }]),
  );
}

function cloneRecentStationHistory(history: PersistedRecentStationEntry[]) {
  return history.map((entry) => ({ ...entry }));
}

function cloneSongHistoryMap(history: Record<string, PersistedSongHistoryEntry[]>) {
  return Object.fromEntries(
    Object.entries(history).map(([stationId, entries]) => [stationId, entries.map((entry) => ({ ...entry }))]),
  );
}

function getLocalStorage(): Storage | null {
  try {
    return globalThis.localStorage ?? null;
  } catch {
    return null;
  }
}

function writeLocalStorageValue(key: string, value: string | null) {
  const storage = getLocalStorage();
  if (!storage) return;

  if (value === null) {
    storage.removeItem(key);
  } else {
    storage.setItem(key, value);
  }
}

function mirrorSettingToLocalStorage<K extends SettingKey>(key: K, value: SettingRecordMap[K]) {
  switch (key) {
    case 'volume':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.volume, typeof value === 'number' ? String(value) : null);
      break;
    case 'lastStationId':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.lastStationId, typeof value === 'string' ? value : null);
      break;
    case 'lastStationCountry':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.lastStationCountry, typeof value === 'string' ? value : null);
      break;
    case 'selectedRadioCountryCodes':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.selectedRadioCountryCodes, JSON.stringify(sanitizeSelectedRadioCountryCodes(value)));
      break;
    case 'castBothMode':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.castBothMode, value ? '1' : '0');
      break;
    case 'favoriteStationIds':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.favoriteStationIds, JSON.stringify(Array.isArray(value) ? value : []));
      break;
    case 'stationUsageCounts':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.stationUsageCounts, JSON.stringify(value && typeof value === 'object' ? value : {}));
      break;
    case 'recentStationHistory':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.recentStationHistory, JSON.stringify(Array.isArray(value) ? value : []));
      break;
    case 'stationSongHistory':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.stationSongHistory, JSON.stringify(value && typeof value === 'object' ? value : {}));
      break;
    case 'stationHealth':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.stationHealth, JSON.stringify(value && typeof value === 'object' ? value : {}));
      break;
    case 'lastExportedAt':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.lastExportedAt, typeof value === 'string' ? value : null);
      break;
    case 'lastImportedAt':
      writeLocalStorageValue(LOCAL_STORAGE_KEYS.lastImportedAt, typeof value === 'string' ? value : null);
      break;
    default:
      break;
  }
}

function mirrorUserStationsToLocalStorage(userStations: unknown[]) {
  writeLocalStorageValue(LOCAL_STORAGE_KEYS.userStations, JSON.stringify(Array.isArray(userStations) ? userStations : []));
}

function clearMirroredLocalStorage() {
  Object.values(LOCAL_STORAGE_KEYS).forEach((key) => writeLocalStorageValue(key, null));
}

function sanitizeImportedSnapshot(source: unknown): PersistedSnapshot {
  const input = source && typeof source === 'object' ? source as Record<string, unknown> : {};
  const volume = typeof input.volume === 'number' && Number.isFinite(input.volume) && input.volume >= 0 && input.volume <= 100
    ? input.volume
    : null;
  const lastStationId = typeof input.lastStationId === 'string' && input.lastStationId.trim().length > 0
    ? input.lastStationId
    : null;
  const lastStationCountry = typeof input.lastStationCountry === 'string' && input.lastStationCountry.trim().length > 0
    ? input.lastStationCountry
    : null;
  const selectedRadioCountryCodes = sanitizeSelectedRadioCountryCodes(input.selectedRadioCountryCodes);
  const castBothMode = input.castBothMode === true;
  const lastExportedAt = typeof input.lastExportedAt === 'string' && input.lastExportedAt.trim().length > 0
    ? input.lastExportedAt
    : null;
  const lastImportedAt = typeof input.lastImportedAt === 'string' && input.lastImportedAt.trim().length > 0
    ? input.lastImportedAt
    : null;
  const favoriteStationIds = Array.isArray(input.favoriteStationIds)
    ? input.favoriteStationIds.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)
    : [];
  const stationUsageCounts = input.stationUsageCounts && typeof input.stationUsageCounts === 'object' && !Array.isArray(input.stationUsageCounts)
    ? Object.fromEntries(
      Object.entries(input.stationUsageCounts as Record<string, unknown>)
        .map(([key, value]) => {
          const numericValue = Number(value);
          return [key, numericValue] as const;
        })
        .filter(([, value]) => Number.isFinite(value) && value >= 0),
    )
    : {};
  const userStations = Array.isArray(input.userStations)
    ? input.userStations
        .filter((entry) => entry && typeof entry === 'object')
        .map((entry, index) => {
          const station = entry as Record<string, unknown>;
          const id = typeof station.id === 'string' && station.id.trim().length > 0 ? station.id : `user-import-${Date.now()}-${index}`;
          return { ...station, id };
        })
    : [];
  const recentStationHistory = sanitizeRecentStationHistory(input.recentStationHistory);
  const stationSongHistory = sanitizeStationSongHistoryMap(input.stationSongHistory);
  const stationHealth = sanitizeStationHealthMap(input.stationHealth);

  return {
    volume,
    lastStationId,
    lastStationCountry,
    selectedRadioCountryCodes,
    castBothMode,
    favoriteStationIds,
    stationUsageCounts,
    recentStationHistory,
    stationSongHistory,
    stationHealth,
    lastExportedAt,
    lastImportedAt,
    userStations,
  };
}

function readLegacyLocalStorage(): PersistedSnapshot {
  const storage = getLocalStorage();
  let volume: number | null = null;
  const rawVolume = storage?.getItem('volume') ?? null;

  if (rawVolume !== null) {
    const parsed = Number(rawVolume);
    if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 100) {
      volume = parsed;
    }
  }

  return {
    volume,
    lastStationId: storage?.getItem('lastStationId') ?? null,
    lastStationCountry: storage?.getItem('lastStationCountry') ?? null,
    selectedRadioCountryCodes: sanitizeSelectedRadioCountryCodes(safeJsonParse<string[]>(storage?.getItem('selectedRadioCountryCodes') ?? null, defaultSelectedRadioCountryCodes)),
    castBothMode: storage?.getItem('castBothMode') === '1',
    favoriteStationIds: safeJsonParse<string[]>(storage?.getItem('favoriteStationIds') ?? null, []).filter(Boolean),
    stationUsageCounts: safeJsonParse<Record<string, number>>(storage?.getItem('stationUsageCounts') ?? null, {}),
    recentStationHistory: sanitizeRecentStationHistory(safeJsonParse<unknown[]>(storage?.getItem('recentStationHistory') ?? null, [])),
    stationSongHistory: sanitizeStationSongHistoryMap(safeJsonParse<Record<string, unknown>>(storage?.getItem('stationSongHistory') ?? null, {})),
    stationHealth: sanitizeStationHealthMap(safeJsonParse<Record<string, unknown>>(storage?.getItem('stationHealth') ?? null, {})),
    lastExportedAt: storage?.getItem('lastExportedAt') ?? null,
    lastImportedAt: storage?.getItem('lastImportedAt') ?? null,
    userStations: safeJsonParse<unknown[]>(storage?.getItem('userStations') ?? null, []),
  };
}

async function migrateFromLocalStorageIfNeeded(db: IDBPDatabase<RadioPlayerDb>) {
  const settingCount = await db.count('settings');
  const userStationCount = await db.count('userStations');

  if (settingCount > 0 || userStationCount > 0) {
    return;
  }

  const legacy = readLegacyLocalStorage();
  const tx = db.transaction(['settings', 'userStations'], 'readwrite');

  await Promise.all([
    tx.objectStore('settings').put(legacy.volume, 'volume'),
    tx.objectStore('settings').put(legacy.lastStationId, 'lastStationId'),
    tx.objectStore('settings').put(legacy.lastStationCountry, 'lastStationCountry'),
    tx.objectStore('settings').put(legacy.selectedRadioCountryCodes, 'selectedRadioCountryCodes'),
    tx.objectStore('settings').put(legacy.castBothMode, 'castBothMode'),
    tx.objectStore('settings').put(legacy.favoriteStationIds, 'favoriteStationIds'),
    tx.objectStore('settings').put(legacy.stationUsageCounts, 'stationUsageCounts'),
    tx.objectStore('settings').put(legacy.recentStationHistory, 'recentStationHistory'),
    tx.objectStore('settings').put(legacy.stationSongHistory, 'stationSongHistory'),
    tx.objectStore('settings').put(legacy.stationHealth, 'stationHealth'),
    tx.objectStore('settings').put(legacy.lastExportedAt, 'lastExportedAt'),
    tx.objectStore('settings').put(legacy.lastImportedAt, 'lastImportedAt'),
    ...legacy.userStations.map((station, index) => {
      const record = station as { id?: string };
      const id = record?.id || `user-${Date.now()}-${index}`;
      return tx.objectStore('userStations').put({
        id,
        sortOrder: index,
        data: typeof station === 'object' && station ? { ...station, id } : { id },
      });
    }),
  ]);

  await tx.done;
}

export async function hydratePlayerPersistence() {
  const db = await getDb();
  if (!db) {
    snapshot = readLegacyLocalStorage();
    hydrated = true;
    return getPlayerPersistenceSnapshot();
  }

  await migrateFromLocalStorageIfNeeded(db);

  const tx = db.transaction(['settings', 'userStations'], 'readonly');
  const settingsStore = tx.objectStore('settings');
  const userStationsStore = tx.objectStore('userStations').index('by-sort-order');
  const volume = await settingsStore.get('volume') as SettingRecordMap['volume'] | undefined;
  const lastStationId = await settingsStore.get('lastStationId') as SettingRecordMap['lastStationId'] | undefined;
  const lastStationCountry = await settingsStore.get('lastStationCountry') as SettingRecordMap['lastStationCountry'] | undefined;
  const selectedRadioCountryCodes = await settingsStore.get('selectedRadioCountryCodes') as SettingRecordMap['selectedRadioCountryCodes'] | undefined;
  const castBothMode = await settingsStore.get('castBothMode') as SettingRecordMap['castBothMode'] | undefined;
  const favoriteStationIds = await settingsStore.get('favoriteStationIds') as SettingRecordMap['favoriteStationIds'] | undefined;
  const stationUsageCounts = await settingsStore.get('stationUsageCounts') as SettingRecordMap['stationUsageCounts'] | undefined;
  const recentStationHistory = await settingsStore.get('recentStationHistory') as SettingRecordMap['recentStationHistory'] | undefined;
  const stationSongHistory = await settingsStore.get('stationSongHistory') as SettingRecordMap['stationSongHistory'] | undefined;
  const stationHealth = await settingsStore.get('stationHealth') as SettingRecordMap['stationHealth'] | undefined;
  const lastExportedAt = await settingsStore.get('lastExportedAt') as SettingRecordMap['lastExportedAt'] | undefined;
  const lastImportedAt = await settingsStore.get('lastImportedAt') as SettingRecordMap['lastImportedAt'] | undefined;
  const userStationRecords = await userStationsStore.getAll();

  await tx.done;

  snapshot = {
    volume: typeof volume === 'number' ? volume : null,
    lastStationId: typeof lastStationId === 'string' ? lastStationId : null,
    lastStationCountry: typeof lastStationCountry === 'string' ? lastStationCountry : null,
    selectedRadioCountryCodes: sanitizeSelectedRadioCountryCodes(selectedRadioCountryCodes),
    castBothMode: castBothMode === true,
    favoriteStationIds: Array.isArray(favoriteStationIds) ? favoriteStationIds.filter(Boolean) : [],
    stationUsageCounts: stationUsageCounts && typeof stationUsageCounts === 'object' && !Array.isArray(stationUsageCounts)
      ? stationUsageCounts
      : {},
    recentStationHistory: sanitizeRecentStationHistory(recentStationHistory),
    stationSongHistory: sanitizeStationSongHistoryMap(stationSongHistory),
    stationHealth: sanitizeStationHealthMap(stationHealth),
    lastExportedAt: typeof lastExportedAt === 'string' ? lastExportedAt : null,
    lastImportedAt: typeof lastImportedAt === 'string' ? lastImportedAt : null,
    userStations: userStationRecords.map((record: UserStationRecord) => record.data),
  };

  hydrated = true;
  return getPlayerPersistenceSnapshot();
}

export function getPlayerPersistenceSnapshot(): PersistedSnapshot {
  if (!hydrated) {
    return {
      volume: DEFAULT_SNAPSHOT.volume,
      lastStationId: DEFAULT_SNAPSHOT.lastStationId,
      lastStationCountry: DEFAULT_SNAPSHOT.lastStationCountry,
      selectedRadioCountryCodes: [...DEFAULT_SNAPSHOT.selectedRadioCountryCodes],
      castBothMode: DEFAULT_SNAPSHOT.castBothMode,
      favoriteStationIds: [...DEFAULT_SNAPSHOT.favoriteStationIds],
      stationUsageCounts: { ...DEFAULT_SNAPSHOT.stationUsageCounts },
      recentStationHistory: cloneRecentStationHistory(DEFAULT_SNAPSHOT.recentStationHistory),
      stationSongHistory: cloneSongHistoryMap(DEFAULT_SNAPSHOT.stationSongHistory),
      stationHealth: cloneStationHealthMap(DEFAULT_SNAPSHOT.stationHealth),
      lastExportedAt: DEFAULT_SNAPSHOT.lastExportedAt,
      lastImportedAt: DEFAULT_SNAPSHOT.lastImportedAt,
      userStations: [...DEFAULT_SNAPSHOT.userStations],
    };
  }

  return {
    volume: snapshot.volume,
    lastStationId: snapshot.lastStationId,
    lastStationCountry: snapshot.lastStationCountry,
    selectedRadioCountryCodes: [...snapshot.selectedRadioCountryCodes],
    castBothMode: snapshot.castBothMode,
    favoriteStationIds: [...snapshot.favoriteStationIds],
    stationUsageCounts: { ...snapshot.stationUsageCounts },
    recentStationHistory: cloneRecentStationHistory(snapshot.recentStationHistory),
    stationSongHistory: cloneSongHistoryMap(snapshot.stationSongHistory),
    stationHealth: cloneStationHealthMap(snapshot.stationHealth),
    lastExportedAt: snapshot.lastExportedAt,
    lastImportedAt: snapshot.lastImportedAt,
    userStations: [...snapshot.userStations],
  };
}

export function getPersistenceBackend(): PlayerPersistenceBackend {
  return persistenceBackend;
}

export function getPersistedVolume() {
  return snapshot.volume;
}

export function getPersistedLastStationId() {
  return snapshot.lastStationId;
}

export function getPersistedLastStationCountry() {
  return snapshot.lastStationCountry;
}

export function getPersistedSelectedRadioCountryCodes() {
  return [...snapshot.selectedRadioCountryCodes];
}

export function getPersistedCastBothMode() {
  return snapshot.castBothMode;
}

export function getPersistedFavoriteStationIds() {
  return new Set(snapshot.favoriteStationIds);
}

export function getPersistedStationUsageCounts() {
  return { ...snapshot.stationUsageCounts };
}

export function getPersistedRecentStationHistory() {
  return cloneRecentStationHistory(snapshot.recentStationHistory);
}

export function getPersistedStationSongHistory() {
  return cloneSongHistoryMap(snapshot.stationSongHistory);
}

export function getPersistedStationHealth() {
  return cloneStationHealthMap(snapshot.stationHealth);
}

export function getPersistedUserStations() {
  return [...snapshot.userStations];
}

async function writeSetting<K extends SettingKey>(key: K, value: SettingRecordMap[K]) {
  mirrorSettingToLocalStorage(key, value);
  const db = await getDb();
  if (!db) return;

  try {
    await db.put('settings', value, key);
  } catch (error) {
    persistenceBackend = 'localstorage';
    console.warn('Failed to persist setting to IndexedDB, continuing with localStorage mirror.', error);
  }
}

export function persistVolume(value: number) {
  snapshot.volume = value;
  void writeSetting('volume', value);
}

export function persistLastStationId(value: string | null) {
  snapshot.lastStationId = value;
  void writeSetting('lastStationId', value);
}

export function persistLastStationCountry(value: string | null) {
  snapshot.lastStationCountry = value;
  void writeSetting('lastStationCountry', value);
}

export function persistSelectedRadioCountryCodes(value: Iterable<string>) {
  snapshot.selectedRadioCountryCodes = sanitizeSelectedRadioCountryCodes(Array.from(value));
  void writeSetting('selectedRadioCountryCodes', snapshot.selectedRadioCountryCodes);
}

export function persistCastBothMode(value: boolean) {
  snapshot.castBothMode = value;
  void writeSetting('castBothMode', value);
}

export function persistFavoriteStationIds(ids: Iterable<string>) {
  snapshot.favoriteStationIds = Array.from(new Set(Array.from(ids).filter(Boolean)));
  void writeSetting('favoriteStationIds', snapshot.favoriteStationIds);
}

export function persistStationUsageCounts(counts: Record<string, number>) {
  snapshot.stationUsageCounts = { ...counts };
  void writeSetting('stationUsageCounts', snapshot.stationUsageCounts);
}

export function persistRecentStationHistory(history: PersistedRecentStationEntry[]) {
  snapshot.recentStationHistory = cloneRecentStationHistory(sanitizeRecentStationHistory(history));
  void writeSetting('recentStationHistory', snapshot.recentStationHistory);
}

export function persistStationSongHistory(history: Record<string, PersistedSongHistoryEntry[]>) {
  snapshot.stationSongHistory = cloneSongHistoryMap(sanitizeStationSongHistoryMap(history));
  void writeSetting('stationSongHistory', snapshot.stationSongHistory);
}

export function persistStationHealth(health: Record<string, PersistedStationHealth>) {
  snapshot.stationHealth = cloneStationHealthMap(sanitizeStationHealthMap(health));
  void writeSetting('stationHealth', snapshot.stationHealth);
}

export function persistLastExportedAt(value: string | null) {
  snapshot.lastExportedAt = value;
  void writeSetting('lastExportedAt', value);
}

export function persistLastImportedAt(value: string | null) {
  snapshot.lastImportedAt = value;
  void writeSetting('lastImportedAt', value);
}

export async function upsertPersistedUserStation(station: unknown) {
  const current = [...snapshot.userStations];
  const incoming = (station ?? {}) as { id?: string };
  const id = incoming.id || `user-${Date.now()}`;
  const nextStation = typeof station === 'object' && station ? { ...station, id } : { id };

  const existingIndex = current.findIndex((entry) => {
    const record = entry as { id?: string };
    return record.id === id;
  });

  const nextList = [...current];
  if (existingIndex >= 0) {
    nextList[existingIndex] = nextStation;
  } else {
    nextList.push(nextStation);
  }

  snapshot.userStations = nextList;
  mirrorUserStationsToLocalStorage(snapshot.userStations);

  const db = await getDb();
  if (!db) {
    return nextStation;
  }

  const tx = db.transaction('userStations', 'readwrite');
  try {
    await tx.store.clear();
    await Promise.all(
      nextList.map((entry, index) => {
        const record = entry as { id?: string };
        return tx.store.put({
          id: record.id || `user-${Date.now()}-${index}`,
          sortOrder: index,
          data: entry,
        });
      }),
    );
    await tx.done;
  } catch (error) {
    persistenceBackend = 'localstorage';
    console.warn('Failed to persist user stations to IndexedDB, continuing with localStorage mirror.', error);
  }

  return nextStation;
}

export async function deletePersistedUserStationAt(index: number) {
  if (index < 0 || index >= snapshot.userStations.length) {
    return;
  }

  snapshot.userStations = snapshot.userStations.filter((_, currentIndex) => currentIndex !== index);
  mirrorUserStationsToLocalStorage(snapshot.userStations);

  const db = await getDb();
  if (!db) {
    return;
  }

  const tx = db.transaction('userStations', 'readwrite');
  try {
    await tx.store.clear();
    await Promise.all(
      snapshot.userStations.map((entry, currentIndex) => {
        const record = entry as { id?: string };
        return tx.store.put({
          id: record.id || `user-${Date.now()}-${currentIndex}`,
          sortOrder: currentIndex,
          data: entry,
        });
      }),
    );
    await tx.done;
  } catch (error) {
    persistenceBackend = 'localstorage';
    console.warn('Failed to delete user station from IndexedDB, continuing with localStorage mirror.', error);
  }
}

export async function deletePersistedUserStationById(id: string | null | undefined) {
  if (!id) {
    return;
  }

  const index = snapshot.userStations.findIndex((entry) => {
    const record = entry as { id?: string };
    return record.id === id;
  });

  if (index < 0) {
    return;
  }

  await deletePersistedUserStationAt(index);
}

export async function importPlayerPersistence(source: unknown) {
  snapshot = sanitizeImportedSnapshot(source);
  hydrated = true;

  mirrorSettingToLocalStorage('volume', snapshot.volume);
  mirrorSettingToLocalStorage('lastStationId', snapshot.lastStationId);
  mirrorSettingToLocalStorage('lastStationCountry', snapshot.lastStationCountry);
  mirrorSettingToLocalStorage('selectedRadioCountryCodes', snapshot.selectedRadioCountryCodes);
  mirrorSettingToLocalStorage('castBothMode', snapshot.castBothMode);
  mirrorSettingToLocalStorage('favoriteStationIds', snapshot.favoriteStationIds);
  mirrorSettingToLocalStorage('stationUsageCounts', snapshot.stationUsageCounts);
  mirrorSettingToLocalStorage('recentStationHistory', snapshot.recentStationHistory);
  mirrorSettingToLocalStorage('stationSongHistory', snapshot.stationSongHistory);
  mirrorSettingToLocalStorage('stationHealth', snapshot.stationHealth);
  mirrorSettingToLocalStorage('lastExportedAt', snapshot.lastExportedAt);
  mirrorSettingToLocalStorage('lastImportedAt', snapshot.lastImportedAt);
  mirrorUserStationsToLocalStorage(snapshot.userStations);

  const db = await getDb();
  if (!db) {
    return getPlayerPersistenceSnapshot();
  }

  const tx = db.transaction(['settings', 'userStations'], 'readwrite');
  try {
    await tx.objectStore('settings').clear();
    await tx.objectStore('userStations').clear();
    await Promise.all([
      tx.objectStore('settings').put(snapshot.volume, 'volume'),
      tx.objectStore('settings').put(snapshot.lastStationId, 'lastStationId'),
      tx.objectStore('settings').put(snapshot.lastStationCountry, 'lastStationCountry'),
      tx.objectStore('settings').put(snapshot.selectedRadioCountryCodes, 'selectedRadioCountryCodes'),
      tx.objectStore('settings').put(snapshot.castBothMode, 'castBothMode'),
      tx.objectStore('settings').put(snapshot.favoriteStationIds, 'favoriteStationIds'),
      tx.objectStore('settings').put(snapshot.stationUsageCounts, 'stationUsageCounts'),
      tx.objectStore('settings').put(snapshot.recentStationHistory, 'recentStationHistory'),
      tx.objectStore('settings').put(snapshot.stationSongHistory, 'stationSongHistory'),
      tx.objectStore('settings').put(snapshot.stationHealth, 'stationHealth'),
      tx.objectStore('settings').put(snapshot.lastExportedAt, 'lastExportedAt'),
      tx.objectStore('settings').put(snapshot.lastImportedAt, 'lastImportedAt'),
      ...snapshot.userStations.map((entry, index) => {
        const record = entry as { id?: string };
        return tx.objectStore('userStations').put({
          id: record.id || `user-import-${Date.now()}-${index}`,
          sortOrder: index,
          data: entry,
        });
      }),
    ]);
    await tx.done;
  } catch (error) {
    persistenceBackend = 'localstorage';
    console.warn('Failed to import data into IndexedDB, continuing with localStorage mirror.', error);
  }

  return getPlayerPersistenceSnapshot();
}

export async function clearPlayerPersistence() {
  snapshot = {
    ...DEFAULT_SNAPSHOT,
    favoriteStationIds: [],
    stationUsageCounts: {},
    recentStationHistory: [],
    stationHealth: {},
    userStations: [],
  };
  hydrated = true;
  clearMirroredLocalStorage();

  const db = await getDb();
  if (!db) {
    return getPlayerPersistenceSnapshot();
  }

  const tx = db.transaction(['settings', 'userStations'], 'readwrite');
  try {
    await tx.objectStore('settings').clear();
    await tx.objectStore('userStations').clear();
    await tx.done;
  } catch (error) {
    persistenceBackend = 'localstorage';
    console.warn('Failed to clear IndexedDB persistence, continuing with localStorage mirror.', error);
  }

  return getPlayerPersistenceSnapshot();
}
