function parseDateWithUtcFallback(value?: string | null): Date | null {
  if (!value) return null;

  const tryParse = (v: string) => {
    const d = new Date(v);
    return isNaN(d.getTime()) ? null : d;
  };

  // If the string has no 'T' but has a space, normalize to ISO with T
  let normalized = value;
  const hasT = value.includes('T');
  if (!hasT && value.includes(' ')) {
    normalized = value.replace(' ', 'T');
  }

  // Determine if incoming value already contains timezone info
  const hasTzInfo = /[zZ]|[+-]\d{2}:?\d{2}$/.test(normalized);

  // If no timezone info, prefer interpreting the value as UTC by appending Z
  if (!hasTzInfo) {
    const utcDate = tryParse(`${normalized}Z`);
    if (utcDate) return utcDate;
  }

  // Fallback: use the original string (covers inputs with explicit timezone)
  const date = tryParse(normalized);
  if (date) return date;

  return null;
}

// Get user timezone from auth data, fallback to Perth
// This function tries to access Inertia page data from the global scope
// For better reliability, components should use the useTimezone composable
function getUserTimezone(): string {
  if (typeof window !== 'undefined') {
    try {
      // Try to access Inertia page data from various possible locations
      // Inertia may store it in different places depending on version
      const possibleLocations = [
        () => (window as any).__inertia?.page?.props?.auth?.user?.timezone,
        () => (window as any).Inertia?.page?.props?.auth?.user?.timezone,
        () => (window as any).Inertia?.router?.page?.props?.auth?.user?.timezone,
        () => (window as any).Inertia?.router?.currentPage?.props?.auth?.user?.timezone,
      ];
      
      for (const getTimezone of possibleLocations) {
        try {
          const timezone = getTimezone();
          if (timezone) {
            return timezone;
          }
        } catch (e) {
          // Continue to next location
        }
      }
    } catch (e) {
      // Fallback if Inertia is not available
    }
  }
  return 'Australia/Perth';
}

export function formatDatePerth(value?: string | null): string {
  const date = parseDateWithUtcFallback(value);
  if (!date) return '-';
  const timezone = getUserTimezone();
  return new Intl.DateTimeFormat('en-AU', {
    timeZone: timezone,
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
  }).format(date);
}

export function formatDateTimePerth(value?: string | null): string {
  const date = parseDateWithUtcFallback(value);
  if (!date) return '-';
  const timezone = getUserTimezone();
  return new Intl.DateTimeFormat('en-AU', {
    timeZone: timezone,
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: true,
  }).format(date);
}

const DMY_DATE_PATTERN = /^\d{2}\/\d{2}\/\d{4}$/;

/** True when value is a complete, valid DD/MM/YYYY date (not a partial mask value). */
export function isCompleteDmyDate(value?: string | null): boolean {
  const raw = (value ?? '').trim();
  if (!raw || !DMY_DATE_PATTERN.test(raw)) {
    return false;
  }

  const [d, m, y] = raw.split('/').map((part) => parseInt(part, 10));
  if (!d || !m || !y) {
    return false;
  }

  const date = new Date(y, m - 1, d);
  return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d;
}

export function parseCompleteDmyDateToYmd(value?: string | null): string | null {
  if (!isCompleteDmyDate(value)) {
    return null;
  }

  const [d, m, y] = (value ?? '').trim().split('/');
  return `${y}-${m}-${d}`;
}

/** Refresh exchange rate when invoice date is cleared or fully entered, not while typing. */
export function shouldRefreshExchangeRateForInvoiceDate(value?: string | null): boolean {
  const raw = (value ?? '').trim();
  return raw === '' || isCompleteDmyDate(raw);
}
