/**
 * Base Repository Pattern
 *
 * Provides abstract repository interface for domain entities
 * with tenant scoping, soft deletes, and pagination.
 *
 * @packageDocumentation
 */
import { createLogger } from '../common/logger.js';
const logger = createLogger({ component: 'repository' });
// ============================================================================
// Base Repository
// ============================================================================
/**
 * Abstract base repository for domain entities
 */
export class BaseRepository {
    entityName;
    constructor(entityName) {
        this.entityName = entityName;
    }
    /**
     * Log repository operation
     */
    logOperation(operation, id, details) {
        logger.debug({ entity: this.entityName, operation, id, ...details }, `${this.entityName} ${operation}`);
    }
}
/**
 * Cached repository wrapper using cache-aside pattern
 */
export class CachedRepository {
    inner;
    cache;
    options;
    constructor(inner, cache, options) {
        this.inner = inner;
        this.cache = cache;
        this.options = options;
    }
    cacheKey(id) {
        return `${this.options.prefix}:${id}`;
    }
    tenantCacheKey(tenantId) {
        return `${this.options.prefix}:tenant:${tenantId}`;
    }
    /**
     * Find by ID with cache
     */
    async findById(id) {
        const key = this.cacheKey(id);
        // Try cache first
        const cached = await this.cache.get(key);
        if (cached) {
            return cached;
        }
        // Fallback to DB
        const entity = await this.inner.findById(id);
        if (entity) {
            await this.cache.set(key, entity, this.options.ttl);
        }
        return entity;
    }
    /**
     * Create with cache invalidation
     */
    async create(entity) {
        const created = await this.inner.create(entity);
        const tenantId = entity.tenantId;
        // Invalidate tenant list cache
        await this.cache.delete(this.tenantCacheKey(tenantId));
        return created;
    }
    /**
     * Update with cache invalidation
     */
    async update(id, updates) {
        const updated = await this.inner.update(id, updates);
        // Invalidate entity cache
        await this.cache.delete(this.cacheKey(id));
        // Invalidate tenant list cache
        await this.cache.delete(this.tenantCacheKey(updated.tenantId));
        return updated;
    }
    /**
     * Delete with cache invalidation
     */
    async softDelete(id) {
        const entity = await this.inner.findById(id);
        if (entity) {
            await this.inner.softDelete(id);
            await this.cache.delete(this.cacheKey(id));
            await this.cache.delete(this.tenantCacheKey(entity.tenantId));
        }
    }
    /**
     * Find by tenant with optional caching
     */
    async findByTenant(tenantId, options) {
        // List results are typically not cached due to complexity
        return this.inner.findByTenant(tenantId, options);
    }
    /**
     * Invalidate cache for an entity
     */
    async invalidate(id) {
        await this.cache.delete(this.cacheKey(id));
    }
    /**
     * Invalidate all cache for a tenant
     */
    async invalidateTenant(tenantId) {
        await this.cache.deletePattern(`${this.options.prefix}:tenant:${tenantId}*`);
    }
}
/**
 * Create a unit of work for transactional operations
 */
export function createUnitOfWork(getConnection) {
    let connection = null;
    return {
        async begin() {
            connection = await getConnection();
            await connection.query('BEGIN');
        },
        async commit() {
            if (connection) {
                await connection.query('COMMIT');
                connection = null;
            }
        },
        async rollback() {
            if (connection) {
                await connection.query('ROLLBACK');
                connection = null;
            }
        },
        async execute(fn) {
            await this.begin();
            try {
                const result = await fn();
                await this.commit();
                return result;
            }
            catch (error) {
                await this.rollback();
                throw error;
            }
        },
    };
}
/**
 * Base specification implementation
 */
export class BaseSpecification {
    and(other) {
        return new AndSpecification(this, other);
    }
    or(other) {
        return new OrSpecification(this, other);
    }
    not() {
        return new NotSpecification(this);
    }
}
class AndSpecification extends BaseSpecification {
    left;
    right;
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }
    isSatisfiedBy(entity) {
        return this.left.isSatisfiedBy(entity) && this.right.isSatisfiedBy(entity);
    }
    toSql() {
        const leftSql = this.left.toSql();
        const rightSql = this.right.toSql();
        return {
            clause: `(${leftSql.clause}) AND (${rightSql.clause})`,
            params: [...leftSql.params, ...rightSql.params],
        };
    }
}
class OrSpecification extends BaseSpecification {
    left;
    right;
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }
    isSatisfiedBy(entity) {
        return this.left.isSatisfiedBy(entity) || this.right.isSatisfiedBy(entity);
    }
    toSql() {
        const leftSql = this.left.toSql();
        const rightSql = this.right.toSql();
        return {
            clause: `(${leftSql.clause}) OR (${rightSql.clause})`,
            params: [...leftSql.params, ...rightSql.params],
        };
    }
}
class NotSpecification extends BaseSpecification {
    spec;
    constructor(spec) {
        super();
        this.spec = spec;
    }
    isSatisfiedBy(entity) {
        return !this.spec.isSatisfiedBy(entity);
    }
    toSql() {
        const sql = this.spec.toSql();
        return {
            clause: `NOT (${sql.clause})`,
            params: sql.params,
        };
    }
}
//# sourceMappingURL=repository.js.map