import type { PaymentSettingsRow } from './paymentService';

interface LocalParts {
  year: number;
  month: number;
  day: number;
  weekday: number;
  minutes: number;
}

const formatterFor = (timeZone: string) => new Intl.DateTimeFormat('en-CA', {
  timeZone, year: 'numeric', month: '2-digit', day: '2-digit',
  weekday: 'short', hour: '2-digit', minute: '2-digit', hourCycle: 'h23',
});

const weekdayValues: Record<string, number> = {
  Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6, Sun: 7,
};

const localParts = (date: Date, timeZone: string): LocalParts => {
  const parts = Object.fromEntries(formatterFor(timeZone).formatToParts(date)
    .filter((part) => part.type !== 'literal').map((part) => [part.type, part.value]));
  return {
    year: Number(parts.year), month: Number(parts.month), day: Number(parts.day),
    weekday: weekdayValues[parts.weekday] ?? 1,
    minutes: Number(parts.hour) * 60 + Number(parts.minute),
  };
};

const zonedDate = (year: number, month: number, day: number, minutes: number, timeZone: string) => {
  const desired = Date.UTC(year, month - 1, day, Math.floor(minutes / 60), minutes % 60);
  let guess = desired;
  for (let index = 0; index < 3; index += 1) {
    const rendered = localParts(new Date(guess), timeZone);
    const renderedValue = Date.UTC(rendered.year, rendered.month - 1, rendered.day,
      Math.floor(rendered.minutes / 60), rendered.minutes % 60);
    guess += desired - renderedValue;
  }
  return new Date(guess);
};

const nextWorkStart = (settings: PaymentSettingsRow, now: Date, includeToday: boolean) => {
  const current = localParts(now, settings.admin_work_timezone);
  for (let offset = includeToday ? 0 : 1; offset <= 8; offset += 1) {
    const calendar = new Date(Date.UTC(current.year, current.month - 1, current.day + offset));
    const weekday = calendar.getUTCDay() === 0 ? 7 : calendar.getUTCDay();
    if (!settings.admin_work_days.includes(weekday)) continue;
    return zonedDate(calendar.getUTCFullYear(), calendar.getUTCMonth() + 1,
      calendar.getUTCDate(), settings.admin_work_start_minutes, settings.admin_work_timezone);
  }
  throw new Error('No working day is configured');
};

export interface PaymentReviewEstimate {
  message: string;
  outsideWorkingHours: boolean;
  estimatedBy: string;
  timeZone: string;
}

export const paymentReviewEstimate = (
  settings: PaymentSettingsRow,
  now = new Date(),
): PaymentReviewEstimate => {
  const local = localParts(now, settings.admin_work_timezone);
  const isWorkday = settings.admin_work_days.includes(local.weekday);
  const withinHours = isWorkday && local.minutes >= settings.admin_work_start_minutes
    && local.minutes < settings.admin_work_end_minutes;
  const fitsToday = withinHours
    && local.minutes + settings.sham_cash_review_minutes <= settings.admin_work_end_minutes;
  if (fitsToday) {
    const estimatedBy = new Date(now.getTime() + settings.sham_cash_review_minutes * 60_000);
    return {
      message: `سيُراجع التحويل خلال مدة لا تتجاوز ${settings.sham_cash_review_minutes} دقائق، وسنبلغك فور اعتماده.`,
      outsideWorkingHours: false,
      estimatedBy: estimatedBy.toISOString(),
      timeZone: settings.admin_work_timezone,
    };
  }
  const beforeOpening = isWorkday && local.minutes < settings.admin_work_start_minutes;
  const opening = nextWorkStart(settings, now, beforeOpening);
  const estimatedBy = new Date(opening.getTime() + settings.sham_cash_after_open_minutes * 60_000);
  const asClock = (minutes: number) => `${String(Math.floor(minutes / 60)).padStart(2, '0')}:${String(minutes % 60).padStart(2, '0')}`;
  const workHours = `${asClock(settings.admin_work_start_minutes)}–${asClock(settings.admin_work_end_minutes)}`;
  const formatted = new Intl.DateTimeFormat('ar', {
    timeZone: settings.admin_work_timezone, weekday: 'long', hour: 'numeric', minute: '2-digit',
  }).format(estimatedBy);
  return {
    message: `فريق المراجعة خارج ساعات العمل الآن (${workHours}). سيُراجع التحويل في أقرب يوم عمل، بحد أقصى حتى ${formatted}.`,
    outsideWorkingHours: true,
    estimatedBy: estimatedBy.toISOString(),
    timeZone: settings.admin_work_timezone,
  };
};
