import { SheetReadCacheConfig } from '../schema/types';
import type { StorageClient } from './types';
export interface CreateSpreadsheetOptions {
    /** Place the spreadsheet inside this Drive folder ID. */
    folderId?: string;
    /** When set, enables supportsAllDrives for Shared Drive placement. */
    sharedDriveId?: string;
}
/**
 * boolean() columns use ONE_OF_LIST too (not a dedicated BOOLEAN type/condition) — see
 * buildValidationRules() in src/utils/validationRules.ts for why that's deliberate.
 */
export interface ColumnValidationRule {
    columnIndex: number;
    type: 'ONE_OF_LIST';
    values: (string | number | boolean)[];
}
export interface SheetFormattingOptions {
    /** Total number of header columns — used for fill/auto-resize ranges. */
    columnCount: number;
    headerColor?: string;
    freezeHeader?: boolean;
    freezeFirstColumn?: boolean;
    validations?: ColumnValidationRule[];
    /** Number of existing data rows (excluding header) — bounds the validation range. Default: 0. */
    dataRowCount?: number;
}
/**
 * Extra rows past the current data range to pre-apply boolean/enum validation to,
 * so a handful of new rows still get checkbox/dropdown UI before the next sync.
 * Left unbounded (the GridRange default), validation extends to the sheet's full
 * 1000-row default grid, and Sheets API reads then treat every one of those
 * formatted-but-empty rows as "has content" — see FAQ.md #10.
 */
export declare const VALIDATION_ROW_BUFFER = 200;
/**
 * How often CRUDOperations.create() re-checks whether the validated range needs
 * extending as rows are appended between syncs (see FAQ.md #10 follow-up). Must be
 * at most half of VALIDATION_ROW_BUFFER so coverage never runs out between checks:
 * a check at row R extends coverage to R + VALIDATION_ROW_BUFFER; the next check at
 * R + VALIDATION_CHECK_INTERVAL must still land inside that window.
 */
export declare const VALIDATION_CHECK_INTERVAL: number;
/** Converts a 0-based column index to A1 column letters (0 -> 'A', 25 -> 'Z', 26 -> 'AA'). */
export declare function columnIndexToA1Letter(index: number): string;
export declare class SheetClient implements StorageClient {
    private sheets;
    private drive;
    private auth;
    private cacheEnabled;
    private cacheTtlMs;
    /** getAllRows() results keyed by `${spreadsheetId}::${sheetName}`, valid until expiresAt. */
    private _readCache;
    /** Collapses concurrent getAllRows() calls for the same key into a single API request. */
    private _inFlightReads;
    constructor(credentials: {
        clientId: string;
        clientSecret: string;
        redirectUri: string;
    }, tokens: unknown, cacheConfig?: SheetReadCacheConfig);
    createSpreadsheet(title: string, options?: CreateSpreadsheetOptions): Promise<string>;
    findOrCreateFolder(name: string, parentId?: string, sharedDriveId?: string): Promise<string>;
    uploadFile(buffer: Buffer, filename: string, mimeType: string, folderId?: string, makePublic?: boolean): Promise<string>;
    deleteFile(fileId: string): Promise<void>;
    addSheet(spreadsheetId: string, sheetName: string): Promise<void>;
    getSheetNames(spreadsheetId: string): Promise<string[]>;
    writeHeader(spreadsheetId: string, sheetName: string, headers: string[]): Promise<void>;
    /**
     * Applies header fill color, frozen rows/columns, auto-fit column widths, and
     * boolean/enum data validation dropdowns in a single batchUpdate call.
     */
    formatSheet(spreadsheetId: string, sheetName: string, options: SheetFormattingOptions): Promise<void>;
    /**
     * Re-applies boolean/enum validation rules bounded to `dataRowCount + VALIDATION_ROW_BUFFER`,
     * without touching header color/freeze/auto-resize. Called by CRUDOperations.create() as rows
     * are appended between syncs, so the validated range keeps pace with real row growth instead
     * of only catching up the next time `lsdb sync` runs — see FAQ.md #10 follow-up.
     */
    extendValidation(spreadsheetId: string, sheetName: string, validations: ColumnValidationRule[], dataRowCount: number): Promise<void>;
    private buildValidationRequests;
    /** Returns the 1-based sheet row number the new row was written to (parsed from the API's updatedRange, no extra read). */
    appendRow(spreadsheetId: string, sheetName: string, values: string[]): Promise<number>;
    appendRows(spreadsheetId: string, sheetName: string, rows: string[][]): Promise<void>;
    /**
     * Reads the full `A:ZZ` range for a tab. Every findMany()/findOne()/count()/update()/delete()
     * call funnels through here, so a single request handler that touches a table more than once
     * (e.g. checkUniqueness() calling findOne() per unique column) — or concurrent requests
     * from different users hitting the same catalog table — used to mean one Sheets API read per
     * call. That's what exhausts Google's default per-user read quota under any real concurrency.
     * A short-TTL cache plus in-flight de-duplication collapses those into a single API call;
     * see FAQ.md #11 for the incident and CacheConfig for tuning/disabling it.
     */
    getAllRows(spreadsheetId: string, sheetName: string): Promise<string[][]>;
    private _fetchAllRows;
    private _cacheKey;
    /** Drops the cached read (if any) for a tab. Called automatically after every write; also exposed for callers that write to a sheet outside this client (e.g. a human editing it directly) and need to force the next read to be fresh. */
    invalidateCache(spreadsheetId: string, sheetName: string): void;
    updateRow(spreadsheetId: string, sheetName: string, rowIndex: number, values: string[]): Promise<void>;
    deleteRow(spreadsheetId: string, sheetName: string, rowIndex: number): Promise<void>;
    shareWithUser(spreadsheetId: string, email: string, role?: 'reader' | 'writer'): Promise<void>;
    /**
     * Deletes an entire tab. Unlike the private getSheetId() used elsewhere (which falls back to
     * sheetId 0 when a title isn't found — fine for formatting calls, not for a delete), this
     * throws if the tab doesn't exist so callers can distinguish "already gone" from "about to
     * delete the wrong tab."
     */
    deleteSheet(spreadsheetId: string, sheetName: string): Promise<void>;
    /**
     * Deletes one or more columns from a tab in a single batchUpdate. `columnIndexes` may be
     * given in any order — they're sorted descending internally so earlier deletions don't shift
     * the indexes of later ones within the same batch.
     */
    deleteColumns(spreadsheetId: string, sheetName: string, columnIndexes: number[]): Promise<void>;
    /**
     * Overwrites a single header cell in place — used for column rename so existing data rows
     * are left untouched (unlike a drop + re-add, which would lose every value in that column).
     */
    updateHeaderCell(spreadsheetId: string, sheetName: string, columnIndex: number, value: string): Promise<void>;
    private getSheetId;
    private getSheetIdStrict;
}
//# sourceMappingURL=sheetClient.d.ts.map