import type { OpeningHoursValue } from '@/components/ui/domain/OpeningHoursFilter';
import type { ViewMode } from '@/components/ui/domain/ViewToggle';
import type { DealType } from '@/lib/deal-types';
import { isDealType } from '@/lib/deal-types';
import { clampInt, csvParse } from '@/lib/url/queryCodec';
import type { UrlCodec } from '@/lib/url/useUrlFilterState';

const DEFAULT_RADIUS = 10;
const RADIUS_MIN = 1;
const RADIUS_MAX = 50;
const DEFAULT_VIEW: ViewMode = 'gallery';

export interface NearYouUrlState {
  cityCode: string | null;
  radius: number;
  hours?: OpeningHoursValue;
  dealTypes: DealType[];
  categoryId?: string;
  tagIds: string[];
  minPrice: number;
  maxPrice: number;
  view: string;
  page: number;
}

function parseOpeningHours(open: string | null): OpeningHoursValue | undefined {
  if (open === 'now') return { mode: 'openNow' };
  if (open?.startsWith('window:')) {
    const parts = open.slice(7).split(',').map(Number);
    if (parts.length === 3) {
      const d = parts[0]!;
      const s = parts[1]!;
      const e = parts[2]!;
      if (!Number.isNaN(d) && !Number.isNaN(s) && !Number.isNaN(e)) {
        return { mode: 'window', dayOfWeek: d, startMin: s, endMin: e };
      }
    }
  }
  return undefined;
}

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

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

export interface MakeNearYouCodecOpts {
  locale: 'he' | 'en';
  priceCeiling: number;
}

function nearYouPath(locale: MakeNearYouCodecOpts['locale']): string {
  return locale === 'en' ? '/en/near-you' : '/near-you';
}

export function makeNearYouCodec(opts: MakeNearYouCodecOpts): UrlCodec<NearYouUrlState> {
  const { locale, priceCeiling } = opts;
  const basePath = nearYouPath(locale);

  return {
    parse(loc) {
      const sp = new URLSearchParams(loc.search);
      const city = sp.get('city');
      const km = sp.get('km');
      const hours = parseOpeningHours(sp.get('open'));
      const typeRaw = sp.get('type');
      const dealTypes: DealType[] = [];
      if (typeRaw) {
        typeRaw.split(',').forEach((v) => {
          if (isDealType(v)) dealTypes.push(v);
        });
      }
      const cat = sp.get('cat');
      const tagIds = csvParse(sp.get('tag'));
      const view = parseView(sp.get('view'));
      const page = clampInt(sp.get('page'), { min: 1, max: Number.MAX_SAFE_INTEGER, fallback: 1 });
      const minPrice = clampInt(sp.get('minPrice'), {
        min: 0,
        max: Number.MAX_SAFE_INTEGER,
        fallback: 0,
      });
      const maxPrice = clampInt(sp.get('maxPrice'), {
        min: 0,
        max: Number.MAX_SAFE_INTEGER,
        fallback: priceCeiling,
      });

      return {
        cityCode: city ?? null,
        radius: clampInt(km, { min: RADIUS_MIN, max: RADIUS_MAX, fallback: DEFAULT_RADIUS }),
        ...(hours !== undefined ? { hours } : {}),
        dealTypes,
        ...(cat ? { categoryId: cat } : {}),
        tagIds,
        minPrice,
        maxPrice: sp.get('maxPrice') == null ? priceCeiling : maxPrice,
        view,
        page,
      };
    },

    build(state) {
      const sp = new URLSearchParams();
      if (state.cityCode) sp.set('city', state.cityCode);
      if (state.radius !== DEFAULT_RADIUS) sp.set('km', String(state.radius));
      const open = serializeOpeningHours(state.hours);
      if (open) sp.set('open', open);
      if (state.dealTypes.length > 0 && state.dealTypes.length < 2) {
        sp.set('type', state.dealTypes.join(','));
      }
      if (state.categoryId) sp.set('cat', state.categoryId);
      if (state.tagIds.length > 0) sp.set('tag', state.tagIds.join(','));
      if (state.minPrice > 0) sp.set('minPrice', String(state.minPrice));
      if (state.maxPrice < priceCeiling) sp.set('maxPrice', String(state.maxPrice));
      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;
    },
  };
}
