import { ErrorCode, ValueTag } from '@bilig/protocol';
const LEADING_ZERO_INTEGER_IDENTIFIER_RE = /^0\d+$/u;
const DECIMAL_COMMA_CELL_RE = /^-?\d+,\d+(%?)$/u;
const PLAIN_DOT_DECIMAL_NUMERIC_RE = /^\d+(?:\.\d+)?$/u;
const GROUPED_DOT_DECIMAL_NUMERIC_RE = /^\d{1,3}(?:,\d{3})+(?:\.\d+)?$/u;
const PLAIN_COMMA_DECIMAL_NUMERIC_RE = /^\d+(?:,\d+)?$/u;
const GROUPED_COMMA_DECIMAL_NUMERIC_RE = /^\d{1,3}(?:\.\d{3})+(?:,\d+)?$/u;
function escapeCsvValue(value) {
    if (!/[",;\t\n\r]/.test(value)) {
        return value;
    }
    return `"${value.replaceAll('"', '""')}"`;
}
export function cellToCsvValue(cell) {
    if (cell.formula !== undefined) {
        return `=${cell.formula}`;
    }
    switch (cell.value.tag) {
        case ValueTag.Empty:
            return '';
        case ValueTag.Number:
            return String(cell.value.value);
        case ValueTag.Boolean:
            return cell.value.value ? 'TRUE' : 'FALSE';
        case ValueTag.String:
            return cell.value.value;
        case ValueTag.Error:
            return `#${ErrorCode[cell.value.code] ?? cell.value.code}`;
    }
}
export function serializeCsv(rows) {
    return rows.map((row) => row.map((value) => escapeCsvValue(value)).join(',')).join('\n');
}
export function resolveCsvParseOptions(csv, options = {}) {
    const delimiter = options.delimiter ?? detectCsvDelimiter(csv);
    return {
        delimiter,
        decimalSeparator: options.decimalSeparator ?? (delimiter !== ',' && hasDecimalCommaCell(csv, delimiter) ? ',' : '.'),
    };
}
export function parseCsv(csv, options = {}) {
    const { delimiter } = resolveCsvParseOptions(csv, options);
    const rows = [];
    let currentRow = [];
    let currentValue = '';
    let index = 0;
    let inQuotes = false;
    while (index < csv.length) {
        const char = csv[index];
        const nextChar = csv[index + 1];
        if (inQuotes) {
            if (char === '"' && nextChar === '"') {
                currentValue += '"';
                index += 2;
                continue;
            }
            if (char === '"') {
                inQuotes = false;
                index += 1;
                continue;
            }
            currentValue += char;
            index += 1;
            continue;
        }
        if (char === '"') {
            inQuotes = true;
            index += 1;
            continue;
        }
        if (char === delimiter) {
            currentRow.push(currentValue);
            currentValue = '';
            index += 1;
            continue;
        }
        if (char === '\r' || char === '\n') {
            currentRow.push(currentValue);
            currentValue = '';
            rows.push(currentRow);
            currentRow = [];
            if (char === '\r' && nextChar === '\n') {
                index += 2;
            }
            else {
                index += 1;
            }
            continue;
        }
        currentValue += char;
        index += 1;
    }
    currentRow.push(currentValue);
    if (currentRow.length > 1 || currentRow[0] !== '' || rows.length > 0) {
        rows.push(currentRow);
    }
    return rows;
}
export function parseCsvCellInput(raw, options = {}) {
    const normalized = raw.trim();
    if (normalized === '') {
        return undefined;
    }
    if (normalized.startsWith('=')) {
        return { formula: normalized.slice(1) };
    }
    if (normalized === 'TRUE' || normalized === 'FALSE') {
        return { value: normalized === 'TRUE' };
    }
    if (isLeadingZeroIntegerIdentifier(normalized)) {
        return { value: raw };
    }
    const accountingNumber = parseAccountingNumberInput(normalized, options.decimalSeparator ?? '.');
    if (accountingNumber !== null) {
        return { value: accountingNumber };
    }
    return { value: raw };
}
function isLeadingZeroIntegerIdentifier(normalized) {
    return LEADING_ZERO_INTEGER_IDENTIFIER_RE.test(normalized);
}
function detectCsvDelimiter(csv) {
    const commaScore = countDelimiterOutsideQuotes(csv, ',');
    const semicolonScore = countDelimiterOutsideQuotes(csv, ';');
    const tabScore = countDelimiterOutsideQuotes(csv, '\t');
    if (tabScore > commaScore && tabScore > semicolonScore) {
        return '\t';
    }
    return semicolonScore > commaScore ? ';' : ',';
}
function countDelimiterOutsideQuotes(csv, delimiter) {
    let count = 0;
    let index = 0;
    let inQuotes = false;
    while (index < csv.length) {
        const char = csv[index];
        const nextChar = csv[index + 1];
        if (inQuotes) {
            if (char === '"' && nextChar === '"') {
                index += 2;
                continue;
            }
            if (char === '"') {
                inQuotes = false;
            }
            index += 1;
            continue;
        }
        if (char === '"') {
            inQuotes = true;
            index += 1;
            continue;
        }
        if (char === delimiter) {
            count += 1;
        }
        index += 1;
    }
    return count;
}
function hasDecimalCommaCell(csv, delimiter) {
    const rows = parseCsv(csv, { delimiter, decimalSeparator: '.' });
    return rows.some((row) => row.some((value) => DECIMAL_COMMA_CELL_RE.test(value.trim())));
}
function parseAccountingNumberInput(normalized, decimalSeparator) {
    let text = normalized;
    let sign = 1;
    if (text.startsWith('(') && text.endsWith(')')) {
        sign = -1;
        text = text.slice(1, -1).trim();
    }
    if (text.startsWith('-')) {
        sign *= -1;
        text = text.slice(1).trim();
    }
    if (text.startsWith('$')) {
        text = text.slice(1).trim();
    }
    if (text.startsWith('-')) {
        sign *= -1;
        text = text.slice(1).trim();
    }
    const isPercent = text.endsWith('%');
    if (isPercent) {
        text = text.slice(0, -1).trim();
    }
    const groupSeparator = decimalSeparator === ',' ? '.' : ',';
    const plainPositiveNumericRe = decimalSeparator === ',' ? PLAIN_COMMA_DECIMAL_NUMERIC_RE : PLAIN_DOT_DECIMAL_NUMERIC_RE;
    const groupedNumericRe = decimalSeparator === ',' ? GROUPED_COMMA_DECIMAL_NUMERIC_RE : GROUPED_DOT_DECIMAL_NUMERIC_RE;
    if (!plainPositiveNumericRe.test(text) && !groupedNumericRe.test(text)) {
        return null;
    }
    const parsed = Number(text.replaceAll(groupSeparator, '').replace(decimalSeparator, '.'));
    if (!Number.isFinite(parsed)) {
        return null;
    }
    return (sign * parsed) / (isPercent ? 100 : 1);
}
//# sourceMappingURL=csv.js.map