/**
 * Pure range algebra for the minimum-stay table (TL-254).
 *
 * Why this exists: `Push_PutMinstay_RQ` OVERLAYS by day, so the server refuses a
 * table that asks for two DIFFERENT values on the same day (the result would
 * depend on send order). Rather than validate-and-reject in the UI, every write
 * goes through `applyRangeToRows`, which carves the target range out of whatever
 * it overlaps before inserting it — the submitted table is correct by
 * construction.
 *
 * Conventions (same as season-range-utils):
 * - dates are ISO `YYYY-MM-DD`; comparisons are lexicographic;
 * - ranges are INCLUSIVE on both bounds;
 * - nothing here reads the clock — `today` is always a parameter.
 */

import { addDays } from './season-range-utils';

export interface MinStayPeriod {
    date_from: string;
    date_to: string;
    min_stay: number;
}

/** A period plus a stable client-side key — Rentals United provides no id. */
export interface MinStayRow extends MinStayPeriod {
    key: string;
}

let rowSeed = 0;

export const nextRowKey = (): string => `msr-${++rowSeed}`;

const byStartDate = (a: MinStayRow, b: MinStayRow): number => a.date_from.localeCompare(b.date_from);

/** Normalizes the server payload into sorted, keyed rows. */
export function toMinStayRows(periods: MinStayPeriod[]): MinStayRow[] {
    return periods
        .map((period) => ({
            key: nextRowKey(),
            date_from: period.date_from,
            date_to: period.date_to,
            min_stay: Number(period.min_stay) || 1,
        }))
        .sort(byStartDate);
}

/** Key-free fingerprint of a table — drives the "unsaved changes" marker. */
export function serializeRows(rows: MinStayRow[]): string {
    return JSON.stringify(rows.map(({ date_from, date_to, min_stay }) => ({ date_from, date_to, min_stay })));
}

/**
 * Removes `[from, to]` from a period. Returns the period untouched when
 * disjoint, one fragment when clipped on a side, two when the range splits it
 * in the middle, and none when fully covered.
 */
export function subtractRange(row: MinStayRow, from: string, to: string): MinStayRow[] {
    if (row.date_to < from || row.date_from > to) return [row];

    const fragments: MinStayRow[] = [];
    if (row.date_from < from) fragments.push({ ...row, key: nextRowKey(), date_to: addDays(from, -1) });
    if (row.date_to > to) fragments.push({ ...row, key: nextRowKey(), date_from: addDays(to, 1) });

    return fragments;
}

/**
 * Sets `minStay` over `[from, to]`, trimming/splitting everything it overlaps.
 * The single primitive behind add, per-row edit and bulk-by-range.
 */
export function applyRangeToRows(rows: MinStayRow[], from: string, to: string, minStay: number): MinStayRow[] {
    return [...rows.flatMap((row) => subtractRange(row, from, to)), { key: nextRowKey(), date_from: from, date_to: to, min_stay: minStay }].sort(
        byStartDate,
    );
}

/** A period the write window can no longer reach: it ends before `today`. */
export function isPastPeriod(row: MinStayPeriod, today: string): boolean {
    return row.date_to < today;
}
