/**
 * Formatting helpers shared by every CRM screen.
 *
 * Dates arrive from the API as ISO 8601 strings and money as numbers, so the
 * conversion to Australian presentation happens here and nowhere else.
 */

const DATE_LOCALE = 'en-AU';

const dateFormatter = new Intl.DateTimeFormat(DATE_LOCALE, {
    day: '2-digit',
    month: 'short',
    year: 'numeric',
});

const dateTimeFormatter = new Intl.DateTimeFormat(DATE_LOCALE, {
    day: '2-digit',
    month: 'short',
    year: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
});

const longDateFormatter = new Intl.DateTimeFormat(DATE_LOCALE, {
    weekday: 'long',
    day: 'numeric',
    month: 'long',
    year: 'numeric',
});

const currencyFormatter = new Intl.NumberFormat(DATE_LOCALE, {
    style: 'currency',
    currency: 'AUD',
    minimumFractionDigits: 2,
});

const compactCurrencyFormatter = new Intl.NumberFormat(DATE_LOCALE, {
    style: 'currency',
    currency: 'AUD',
    notation: 'compact',
    maximumFractionDigits: 1,
});

const numberFormatter = new Intl.NumberFormat(DATE_LOCALE);

function parse(value: string | null | undefined): Date | null {
    if (!value) {
        return null;
    }

    const date = new Date(value);

    return Number.isNaN(date.getTime()) ? null : date;
}

export function formatDate(value: string | null | undefined): string {
    const date = parse(value);

    return date ? dateFormatter.format(date) : '—';
}

export function formatDateTime(value: string | null | undefined): string {
    const date = parse(value);

    return date ? dateTimeFormatter.format(date) : '—';
}

export function formatLongDate(value: string | null | undefined): string {
    const date = parse(value);

    return date ? longDateFormatter.format(date) : '—';
}

/**
 * A date input value (yyyy-mm-dd) for a stored timestamp.
 */
export function toDateInputValue(value: string | null | undefined): string {
    const date = parse(value);

    if (!date) {
        return '';
    }

    return [
        date.getFullYear(),
        String(date.getMonth() + 1).padStart(2, '0'),
        String(date.getDate()).padStart(2, '0'),
    ].join('-');
}

/**
 * A datetime-local input value for a stored timestamp.
 */
export function toDateTimeInputValue(value: string | null | undefined): string {
    const date = parse(value);

    if (!date) {
        return '';
    }

    return `${toDateInputValue(value)}T${String(date.getHours()).padStart(2, '0')}:${String(
        date.getMinutes(),
    ).padStart(2, '0')}`;
}

export function formatCurrency(value: number | null | undefined): string {
    if (value === null || value === undefined) {
        return '—';
    }

    return currencyFormatter.format(value);
}

export function formatCompactCurrency(value: number | null | undefined): string {
    if (value === null || value === undefined) {
        return '—';
    }

    return compactCurrencyFormatter.format(value);
}

export function formatNumber(value: number | null | undefined): string {
    if (value === null || value === undefined) {
        return '—';
    }

    return numberFormatter.format(value);
}

export function formatKilowatts(value: number | null | undefined): string {
    if (value === null || value === undefined) {
        return '—';
    }

    return `${numberFormatter.format(value)} kW`;
}

/**
 * "3 days ago" style relative time, used for job aging.
 */
export function formatRelative(value: string | null | undefined): string {
    const date = parse(value);

    if (!date) {
        return '—';
    }

    const seconds = Math.round((Date.now() - date.getTime()) / 1000);
    const formatter = new Intl.RelativeTimeFormat(DATE_LOCALE, { numeric: 'auto' });

    const thresholds: [Intl.RelativeTimeFormatUnit, number][] = [
        ['year', 31536000],
        ['month', 2592000],
        ['week', 604800],
        ['day', 86400],
        ['hour', 3600],
        ['minute', 60],
    ];

    for (const [unit, secondsInUnit] of thresholds) {
        if (Math.abs(seconds) >= secondsInUnit) {
            return formatter.format(-Math.round(seconds / secondsInUnit), unit);
        }
    }

    return 'just now';
}

/**
 * Days a job has been sitting in its current status, phrased for humans.
 */
export function formatDuration(days: number | null | undefined): string {
    if (days === null || days === undefined) {
        return '—';
    }

    if (days <= 0) {
        return 'Today';
    }

    if (days === 1) {
        return '1 day';
    }

    return `${days} days`;
}

export function initials(value: string): string {
    return value
        .split(/\s+/)
        .filter(Boolean)
        .slice(0, 2)
        .map((part) => part.charAt(0).toUpperCase())
        .join('');
}

export function formatAddress(
    parts: Array<string | null | undefined>,
): string {
    return parts.filter((part) => Boolean(part && part.trim())).join(', ');
}
