/** UTC+8 wall-clock as `YYYY-MM-DD HH:MM:SS` — matches SQLite's `datetime()`
 *  output shape so cross-source comparisons are well-defined. */
export declare function nowSqliteLocal(): string;
/** Convert an absolute Date (or ms) to the local-storage shape. */
export declare function toSqliteLocal(d: Date | number): string;
/** Normalize whatever the agent passes for `when_at` into our storage shape.
 *  Accepts: bare ISO (assumed already local), ISO with `Z` or `±HH:MM` offset,
 *  or already-normalized "YYYY-MM-DD HH:MM:SS". Output: "YYYY-MM-DD HH:MM:SS"
 *  in UTC+8. Falls through unchanged on parse failure rather than throwing
 *  so an agent typo doesn't kill the save. */
export declare function normalizeWhenAt(s: string | null | undefined): string | null;
export type MemoSource = 'manual' | 'agent' | 'geocoded' | 'browser';
export interface Memo {
    id: number;
    platform: string;
    channel_id: string;
    thread_id: string;
    user_id: string;
    what: string;
    who: string | null;
    when_at: string | null;
    when_text: string | null;
    where_lat: number | null;
    where_lng: number | null;
    where_label: string | null;
    how: string | null;
    why: string | null;
    /** Free-text full intent — preserves the user's complete phrasing when
     *  `what` is an extracted short noun. Optional; null when unset. */
    memo: string | null;
    expires_at: string | null;
    source: MemoSource;
    created_at: string;
    updated_at: string;
}
export interface OwnerOpts {
    /** Scope reads/writes/deletes to this user. Empty = unrestricted (tests
     *  / non-IM callers only). Production calls always pass userId. */
    userId?: string;
}
export interface CreateMemoInput {
    platform: string;
    channelId: string;
    threadId: string;
    userId?: string;
    what: string;
    who?: string | null;
    whenAt?: string | null;
    whenText?: string | null;
    whereLat?: number | null;
    whereLng?: number | null;
    whereLabel?: string | null;
    how?: string | null;
    why?: string | null;
    /** Free-text full intent — preserve user's complete phrasing when `what`
     *  is a shorter extracted noun. Optional. */
    memo?: string | null;
    expiresAt?: string | null;
    source?: MemoSource;
}
export declare function createMemo(input: CreateMemoInput): number;
export interface UpdateMemoInput {
    what?: string;
    who?: string | null;
    whenAt?: string | null;
    whenText?: string | null;
    whereLat?: number | null;
    whereLng?: number | null;
    whereLabel?: string | null;
    how?: string | null;
    why?: string | null;
    memo?: string | null;
    expiresAt?: string | null;
}
/** Patch-style update — only fields explicitly present in the input are
 *  written. Pass `null` to clear a field (vs `undefined` = leave alone). */
export declare function updateMemo(id: number, input: UpdateMemoInput, opts?: OwnerOpts): boolean;
export declare function getMemo(id: number, opts?: OwnerOpts): Memo | null;
export declare function deleteMemo(id: number, opts?: OwnerOpts): boolean;
export interface SearchOpts extends OwnerOpts {
    /** Free-text — matched as `LIKE %query%` against what / who / where_label /
     *  how / why / when_text. Single token; no AND splitting. */
    query?: string;
    /** LIKE filter on `who` only. */
    who?: string;
    /** LIKE filter on `what` only. */
    what?: string;
    /** When true, filter to rows where where_lat IS NOT NULL. */
    hasLocation?: boolean;
    /** ISO range on `when_at` (or fall back to `created_at` when when_at is null). */
    whenAfter?: string;
    whenBefore?: string;
    /** When true, include expired rows in the result. Default: exclude. */
    includeExpired?: boolean;
    limit?: number;
}
/**
 * Search memos. All filters are AND-combined. Empty filter set returns the
 * most recent memos (= same as listMemos).
 *
 * Sort: `created_at DESC` (newest first). Future: relevance score.
 *
 * Wildcard escaping: SQL LIKE `%` and `_` in the query become literal chars
 * via `LIKE ESCAPE '\\'` — the user's question-mark or percent doesn't blow
 * the search wide open.
 */
export declare function searchMemos(opts?: SearchOpts): Memo[];
/** Recent memos for this user. Convenience wrapper around searchMemos. */
export declare function listMemos(opts?: OwnerOpts & {
    limit?: number;
}): Memo[];
export declare function sweepExpired(): number;
export declare function startSweepTimer(): void;
export declare function stopSweepTimer(): void;
export declare function closeMemosDb(): void;
export declare function parseCoordPair(input: string): {
    lat: number;
    lng: number;
} | null;
export declare function stripCoordPrefix(input: string): string;
export type GeocodeOutcome = {
    ok: true;
    lat: number;
    lng: number;
    formattedAddress: string;
} | {
    ok: false;
    reason: 'no_key' | 'quota' | 'signature' | 'not_found' | 'http' | 'unknown';
    status?: number;
    message?: string;
};
export declare function geocodeAddress(address: string): Promise<GeocodeOutcome>;
export declare function isGeocodingAvailable(): boolean;
export declare function wgs84ToGcj02(lat: number, lng: number): {
    lat: number;
    lng: number;
};
/**
 * Reverse of wgs84ToGcj02. Useful when an upstream gives us GCJ-02 coords
 * but our DB convention is WGS-84.
 *
 * Common case: iOS Core Location in China returns GCJ-02 by design (Apple
 * applies the offset to comply with PRC regulations). Telegram on a Chinese
 * iPhone forwards exactly those coords. Without this reverse, the values
 * get re-offset when we build amap/baidu URLs → marker lands 500m–1km off.
 *
 * Math: GCJ→WGS doesn't have a closed-form inverse, so we use the standard
 * Newton's-method-style refinement — compute the forward offset at the
 * GCJ point, subtract to get a candidate WGS point, then iterate. Three
 * passes converge to sub-meter precision for any reasonable input.
 */
export declare function gcj02ToWgs84(lat: number, lng: number): {
    lat: number;
    lng: number;
};
export interface MapUrls {
    baidu: string;
    amap: string;
    google: string;
}
export declare function mapUrls(lat: number, lng: number, label?: string, addr?: string): MapUrls;
export declare function mapUrl(lat: number, lng: number, label?: string, addr?: string): string;
//# sourceMappingURL=memos.d.ts.map