import { PgTableWithColumns } from 'drizzle-orm/pg-core';
import { DrizzleTx } from '../types/drizzle.js';
import { BaseRepository } from './base-repository.js';
import 'drizzle-orm/node-postgres';
import 'drizzle-orm';

interface JunctionIntegrationConfig {
    /** Left endpoint: local FK column (camel) + strict parent table. */
    left: {
        column: string;
        refTable: PgTableWithColumns<any>;
    };
    /** Right endpoint: local FK column (camel) + strict parent table. */
    right: {
        column: string;
        refTable: PgTableWithColumns<any>;
    };
    /** Role column (camel), or null for a role-less (2-part composite) junction. */
    roleColumn: string | null;
}
declare abstract class JunctionIntegrationRepository<TEntity, TIntegrationWrite, TIntegrationProjection> extends BaseRepository<TEntity> {
    /**
     * Declarative junction integration surface. Concrete repos declare this — the
     * template emits it with live parent-table handles.
     */
    protected abstract readonly integrationConfig: JunctionIntegrationConfig;
    /**
     * Upsert ONE junction row by its composite identity, in a single transaction:
     *   1. resolve the REQUIRED left FK (provider-scoped) — STRICT: missing → throws;
     *   2. resolve the REQUIRED right FK (provider-scoped) — STRICT: missing → throws;
     *   3. insert-or-update on the `(left, right[, role])` conflict target.
     *
     * Idempotent. Returns the composite externalId as the projection `id`.
     *
     * @param write     parent external ids (`<left>ExternalId`/`<right>ExternalId`)
     *                  + optional `role` + `userId`
     * @param provider  adapter/provider label used to scope the parent lookups
     * @param tx        optional outer transaction; when omitted we open our own
     */
    integrationUpsertOne(write: TIntegrationWrite, provider: string, tx?: DrizzleTx): Promise<TIntegrationProjection>;
    /**
     * Canonical-projected lookup by the COMPOSITE externalId, differ-ready. Parses
     * the composite, resolves BOTH parents NON-throwing (→ null), then selects by
     * the identity tuple. Returns `null` on malformed composite / unresolved
     * parent / no row (a missing "before" side is a create from the differ's view).
     */
    findByExternalIdProjected(externalId: string, provider: string): Promise<TIntegrationProjection | null>;
    /**
     * Hard-delete the junction by composite externalId. Junctions have no
     * `deleted_at` and no external-linkage columns to clear, so a integration "delete"
     * removes the row. Resolves both parents NON-throwing, then deletes by the
     * identity tuple. Returns the composite id, or `null` when nothing matched.
     */
    softDeleteByExternalId(externalId: string, provider: string, tx?: DrizzleTx): Promise<{
        id: string;
    } | null>;
    /**
     * Project a raw junction row to the differ shape: the COMPOSITE externalId
     * `id` (junctions have no surrogate id) plus the local FK columns, role (when
     * role-bearing) and timestamps — matching the emitted
     * `<Junction>IntegrationProjection` interface (id + leftId + rightId + role? +
     * createdAt + updatedAt). Junction projections are purely structural, so this
     * is fully generic over `integrationConfig` — no per-junction override is emitted.
     */
    protected toProjection(row: TEntity, write: Record<string, unknown>, _provider: string): TIntegrationProjection;
    /** Build the identity WHERE clause `(left, right[, role])`. */
    private identityWhere;
    /** Resolve a parent id (provider-scoped), throwing when unresolved. */
    private resolveStrict;
    /** Resolve a parent id (provider-scoped), returning null when unresolved. */
    private resolveLoose;
}
/**
 * Build the composite externalId from the two parent external ids (+ role when
 * the junction is role-bearing).
 */
declare function buildCompositeExternalId(leftExternalId: string, rightExternalId: string, role?: string): string;
/**
 * Parse a composite externalId. `withRole` selects the expected part count
 * (3 when role-bearing, else 2). Returns `null` when the shape doesn't match
 * or any part is empty.
 */
declare function parseCompositeExternalId(externalId: string, withRole: boolean): {
    left: string;
    right: string;
    role: string | undefined;
} | null;

export { type JunctionIntegrationConfig, JunctionIntegrationRepository, buildCompositeExternalId, parseCompositeExternalId };
