// Einfacher Opening-Hours-Parser fuer haeufige OSM-Formate wie
// "Mo-Su 11:00-22:00" oder "Mo-Fr 11:00-14:00,17:00-22:00; Sa-Su 11:00-22:00".
// Deckt die gaengigsten Faelle ab, ist aber kein vollstaendiger osm-opening_hours-Parser.
import tzLookup from 'tz-lookup';

const WEEKDAY_MAP: Record<string, number> = { Mo: 1, Tu: 2, We: 3, Th: 4, Fr: 5, Sa: 6, Su: 0 };

interface TimeRange { start: number; end: number; } // Minuten seit Mitternacht

function parseTimeRange(str: string): TimeRange | null {
  const m = str.match(/(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})/);
  if (!m) return null;
  const start = parseInt(m[1]) * 60 + parseInt(m[2]);
  const end = parseInt(m[3]) * 60 + parseInt(m[4]);
  return { start, end };
}

function expandDayRange(dayStr: string): number[] {
  if (dayStr.includes('-')) {
    const [from, to] = dayStr.split('-');
    const fromNum = WEEKDAY_MAP[from];
    const toNum = WEEKDAY_MAP[to];
    if (fromNum === undefined || toNum === undefined) return [];
    const days: number[] = [];
    let d = fromNum;
    while (true) {
      days.push(d);
      if (d === toNum) break;
      d = (d + 1) % 7;
    }
    return days;
  }
  return WEEKDAY_MAP[dayStr] !== undefined ? [WEEKDAY_MAP[dayStr]] : [];
}

export interface OpeningStatus {
  isOpen: boolean | null; // null = unbekannt/nicht parsebar
  label: string;
}

// i18n-Schritt 9 (letzter Schritt): die fuenf moeglichen Status-Labels sind
// jetzt ueber diesen Typ injizierbar statt hartcodiert Deutsch. lib/i18n/de.ts
// und en.ts liefern jeweils ein Objekt dieser Form (Dictionary.openingHours).
// DEFAULT_LABELS_DE bleibt als Fallback bestehen, falls diese Funktion von
// einer noch nicht migrierten Stelle ohne Labels-Parameter aufgerufen wird -
// dann verhaelt sie sich exakt wie vor dieser Aenderung (immer Deutsch).
export interface OpeningHoursLabels {
  unknown: string;
  currentlyClosed: string;
  open247: string;
  unclear: string;
  openNow: string;
}

const DEFAULT_LABELS_DE: OpeningHoursLabels = {
  unknown: 'Öffnungszeiten unbekannt',
  currentlyClosed: 'Aktuell geschlossen',
  open247: 'Durchgehend geöffnet',
  unclear: 'Öffnungszeiten unklar',
  openNow: 'Jetzt geöffnet',
};

const FALLBACK_TIMEZONE = 'Europe/Berlin';

export function resolveRestaurantTimezone(lat?: number | null, lon?: number | null): string {
  if (lat == null || lon == null) return FALLBACK_TIMEZONE;
  try {
    return tzLookup(lat, lon);
  } catch {
    return FALLBACK_TIMEZONE;
  }
}

function getLocalParts(date: Date, timeZone: string): { weekday: number; minutes: number } {
  const parts = new Intl.DateTimeFormat('en-US', {
    timeZone,
    weekday: 'short',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).formatToParts(date);

  const weekdayStr = parts.find((p) => p.type === 'weekday')?.value ?? 'Sun';
  const hourStr = parts.find((p) => p.type === 'hour')?.value ?? '0';
  const minuteStr = parts.find((p) => p.type === 'minute')?.value ?? '0';

  const weekdayMap: Record<string, number> = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
  return {
    weekday: weekdayMap[weekdayStr] ?? 0,
    minutes: parseInt(hourStr) * 60 + parseInt(minuteStr),
  };
}

export function evaluateOpeningStatus(
  raw: string | null | undefined,
  now: Date = new Date(),
  timeZone: string = FALLBACK_TIMEZONE,
  labels: OpeningHoursLabels = DEFAULT_LABELS_DE,
): OpeningStatus {
  if (!raw) return { isOpen: null, label: labels.unknown };

  // OSM erlaubt "closed"/"off" als eigenstaendigen opening_hours-Wert (bedeutet
  // "aktuell/dauerhaft geschlossen", z.B. bei temporaer geschlossenen Betrieben).
  const normalized = raw.trim().toLowerCase();
  if (normalized === 'closed' || normalized === 'off') {
    return { isOpen: false, label: labels.currentlyClosed };
  }

  if (normalized.includes('24/7')) return { isOpen: true, label: labels.open247 };

  const { weekday: currentDay, minutes: currentMinutes } = getLocalParts(now, timeZone);

  const segments = raw.split(';').map((s) => s.trim());
  let matchedToday = false;
  let openNow = false;

  for (const segment of segments) {
    const parts = segment.split(' ');
    if (parts.length < 2) continue;
    const dayPart = parts[0];
    const timePart = parts.slice(1).join(' ');
    const days = dayPart.split(',').flatMap(expandDayRange);
    if (!days.includes(currentDay)) continue;

    matchedToday = true;
    const ranges = timePart.split(',').map(parseTimeRange).filter(Boolean) as TimeRange[];
    for (const r of ranges) {
      if (currentMinutes >= r.start && currentMinutes <= r.end) {
        openNow = true;
      }
    }
  }

  // Statt den rohen, nicht-parsebaren OSM-String 1:1 als Label anzuzeigen
  // (z.B. kryptische Sonderregeln), lieber ein freundlicher, generischer
  // Hinweis in der jeweiligen Sprache. Der volle Rohtext bleibt separat im
  // Detail-Sheet sichtbar.
  if (!matchedToday) return { isOpen: null, label: labels.unclear };

  return {
    isOpen: openNow,
    label: openNow ? labels.openNow : labels.currentlyClosed,
  };
}
