import { clampInt } from '@/lib/url/queryCodec';
import type { UrlCodec } from '@/lib/url/useUrlFilterState';
import type { OpeningHoursValue } from '@/components/ui/domain/OpeningHoursFilter';

export type StoresSortBy = 'rating' | 'deals';
export type StoresView = 'grid' | 'map';

const SORTS = new Set<StoresSortBy>(['rating', 'deals']);
const DEFAULT_SORT: StoresSortBy = 'rating';
const DEFAULT_VIEW: StoresView = 'grid';

function parseSort(raw: string | null): StoresSortBy {
  if (raw && SORTS.has(raw as StoresSortBy)) return raw as StoresSortBy;
  return DEFAULT_SORT;
}

function parseView(raw: string | null): StoresView {
  return raw === 'map' ? 'map' : DEFAULT_VIEW;
}

export interface StoresUrlState extends Record<string, unknown> {
  query: string;
  city: string | null;
  businessTypeId: string | null;
  hours: OpeningHoursValue;
  sortBy: StoresSortBy;
  view: StoresView;
  page: number;
}

export function parseStoresHoursParam(raw: string | null): OpeningHoursValue | null {
  if (raw === 'now') return { mode: 'openNow' };
  if (raw === null || raw === '') return { mode: 'any' };
  if (!raw.startsWith('window:')) return null;

  const [dayOfWeek, startMin, endMin] = raw.slice(7).split(',').map(Number);
  if (
    dayOfWeek === undefined ||
    startMin === undefined ||
    endMin === undefined ||
    !Number.isInteger(dayOfWeek) ||
    dayOfWeek < 0 ||
    dayOfWeek > 6 ||
    !Number.isInteger(startMin) ||
    startMin < 0 ||
    startMin > 1439 ||
    !Number.isInteger(endMin) ||
    endMin < 0 ||
    endMin > 1439
  ) {
    return null;
  }
  return { mode: 'window', dayOfWeek, startMin, endMin };
}

export function serializeStoresHoursParam(hours: OpeningHoursValue): string | null {
  if (hours.mode === 'openNow') return 'now';
  if (hours.mode === 'window') {
    return `window:${hours.dayOfWeek},${hours.startMin},${hours.endMin}`;
  }
  return null;
}

export function makeStoresCodec(basePath: string): UrlCodec<StoresUrlState> {
  return {
    parse(loc) {
      const sp = new URLSearchParams(loc.search);
      return {
        query: sp.get('q')?.trim() ?? '',
        city: sp.get('city') || null,
        businessTypeId: sp.get('businessType') || null,
        hours: parseStoresHoursParam(sp.get('open')) ?? { mode: 'any' },
        sortBy: parseSort(sp.get('sort')),
        view: parseView(sp.get('view')),
        page: clampInt(sp.get('page'), { min: 1, max: Number.MAX_SAFE_INTEGER, fallback: 1 }),
      };
    },

    build(state) {
      const sp = new URLSearchParams();
      const query = state.query.trim();
      if (query) sp.set('q', query);
      if (state.city) sp.set('city', state.city);
      if (state.businessTypeId) sp.set('businessType', state.businessTypeId);
      const hours = serializeStoresHoursParam(state.hours);
      if (hours) sp.set('open', hours);
      if (state.sortBy !== DEFAULT_SORT) sp.set('sort', state.sortBy);
      if (state.view !== DEFAULT_VIEW) sp.set('view', state.view);
      if (state.page >= 2) sp.set('page', String(state.page));
      const qs = sp.toString();
      return qs ? `${basePath}?${qs}` : basePath;
    },
  };
}
