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

declare abstract class IntegratedEntityRepository<TEntity, TIntegrationWrite = Partial<TEntity>, TIntegrationProjection = TEntity> extends BaseRepository<TEntity> {
    /**
     * Declarative integration write surface. Concrete (`pattern: Integrated`) repositories
     * declare this — the template emits it from the entity's fields + FKs.
     */
    protected abstract readonly integrationConfig: IntegrationUpsertConfig;
    /**
     * Find a single entity by its external CRM identifier.
     */
    findByExternalId(externalId: string): Promise<TEntity | null>;
    /**
     * Find multiple entities by external CRM identifiers.
     */
    findManyByExternalIds(externalIds: string[]): Promise<TEntity[]>;
    /**
     * Find all entities owned by a specific user.
     */
    findAllByUserId(userId: string): Promise<TEntity[]>;
    /**
     * Upsert ONE entity by its `(provider, externalId)` identity, in a single
     * transaction:
     *   1. resolve each `integrationConfig.fkResolvers` FK (provider-scoped). Strict
     *      resolvers throw on unresolved; non-strict leave the column null.
     *   2. insert-or-update the canonical columns via `onConflictDoUpdate` on the
     *      `conflictTarget`. Resolved FKs are only written into `set` when
     *      non-null this run (no-clobber).
     *   3. EAV dual-write of `write.fields` via `writeCustomFields` when
     *      `integrationConfig.eav` and the bag is non-empty (same tx).
     *
     * Idempotent: a second call with the same identity updates in place. Returns
     * the canonical projection (so the orchestrator records `local_id`).
     *
     * @param write     canonical fields + parent external ids + custom-field bag
     * @param provider  adapter/provider label persisted + used to scope 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 external id (differ-ready). Returns `null`
     * when no local row exists. Provider-scoped so a HubSpot id can't match a
     * Salesforce row.
     */
    findByExternalIdProjected(externalId: string, provider: string): Promise<TIntegrationProjection | null>;
    /**
     * Integration "delete" by external id, provider-scoped. When `softDelete: true`,
     * sets `deletedAt`. When `softDelete: false`, tombstone-by-clearing: null out
     * `external_id`/`provider` so the row no longer matches future inbound
     * changes while preserving local-id references. Returns `{ id }` or `null`.
     */
    softDeleteByExternalId(externalId: string, provider: string, tx?: DrizzleTx): Promise<{
        id: string;
    } | null>;
    /**
     * Batch integration upsert — concretizes the former abstract stub. Delegates to
     * `integrationUpsertOne` per input inside one transaction. Inputs are raw partial
     * rows: provider is read from each input's own `provider` column; rows
     * missing `externalId`/`provider` are skipped.
     */
    integrationUpsert(inputs: Array<Partial<TEntity>>): Promise<TEntity[]>;
    /**
     * Project a raw row to the canonical differ shape — a generic pick over
     * `integrationConfig.projectionColumns`. Override only for synthesized projections
     * (e.g. junctions); entities use this verbatim.
     */
    protected toProjection(row: TEntity): TIntegrationProjection;
    /**
     * EAV dual-write seam (#374, live path lands in #124). No-op by default;
     * `eav: true` entities emit a concrete override that injects
     * `FieldValueService` and delegates to `upsertFieldsTransactional` so the
     * dual-write joins the same tx (`db`). Kept as an explicit hook so the base
     * stays portable (the FieldValueService dependency is eav-only).
     */
    protected writeCustomFields(_db: DrizzleTx, _entityId: string, _userId: string, _fields: Record<string, unknown>): Promise<void>;
    /**
     * Resolve one FK from a parent external id (provider-scoped). `self` resolves
     * against `this.table`. Strict resolvers throw when unresolved; non-strict
     * return null. A null/absent write value short-circuits to null.
     */
    private resolveFk;
    /**
     * Find entities visible to a user (ownership + sharing rules).
     * Concrete repositories must implement with visibility logic.
     */
    findVisibleByUserId(_userId: string): Promise<TEntity[]>;
}

export { IntegratedEntityRepository };
