/**
 * auditRepository.ts
 *
 * Cosmos-backed persistence for TradeAuditEvent records.
 *
 * Container: audit-events
 * Partition key: /productId
 *
 * Write policy: best-effort — audit persistence failures are logged and swallowed;
 * they must never block the primary operation that triggered the audit event.
 *
 * The container name intentionally uses a hyphen to match Azure naming conventions.
 * When creating the container in Cosmos, set:
 *   - Partition key: /productId
 *   - TTL: 7776000 (90 days) — set at container level, not per-document
 */

import { getContainer } from '@bytelyst/cosmos';
import { config } from '../config/index.js';
import logger from '../utils/logger.js';

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface AuditEventRecord {
    event: string;
    userId?: string;
    profileId?: string;
    symbol?: string;
    outcome?: 'accepted' | 'rejected' | 'error';
    reason?: string;
    details?: Record<string, unknown>;
}

interface AuditEventDocument extends AuditEventRecord {
    /** Cosmos document id — UUID generated at write time */
    id: string;
    productId: string;
    /** ISO-8601 timestamp */
    ts: string;
    /** Unix epoch ms — enables numeric range queries */
    tsMs: number;
}

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

const CONTAINER_NAME = 'audit-events';

function isCosmosConfigured(): boolean {
    return Boolean(config.COSMOS_ENDPOINT && config.COSMOS_KEY);
}

function generateAuditId(): string {
    // Use crypto.randomUUID if available (Node ≥ 18), otherwise fall back to a
    // timestamp + random suffix that is unique enough for append-only audit records.
    if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
        return crypto.randomUUID();
    }
    return `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
}

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

/**
 * Persist a single audit event to Cosmos.
 *
 * Returns true if the write succeeded, false if Cosmos is not configured or
 * the write failed. Never throws.
 */
export async function persistAuditEvent(event: AuditEventRecord): Promise<boolean> {
    if (!isCosmosConfigured()) {
        return false;
    }

    try {
        const container = getContainer(CONTAINER_NAME);
        const now = new Date();
        const doc: AuditEventDocument = {
            id: generateAuditId(),
            productId: config.PRODUCT_ID,
            ts: now.toISOString(),
            tsMs: now.getTime(),
            event: event.event,
            ...(event.userId !== undefined && { userId: event.userId }),
            ...(event.profileId !== undefined && { profileId: event.profileId }),
            ...(event.symbol !== undefined && { symbol: event.symbol }),
            ...(event.outcome !== undefined && { outcome: event.outcome }),
            ...(event.reason !== undefined && { reason: event.reason }),
            ...(event.details !== undefined && { details: event.details }),
        };
        await container.items.create(doc);
        return true;
    } catch (error) {
        logger.warn(`[Audit] Cosmos persist failed: ${error instanceof Error ? error.message : 'unknown error'}`);
        return false;
    }
}

/**
 * Query recent audit events for a given user, optionally filtered by event type.
 * Returns results ordered newest-first, capped at `limit` (default 100).
 *
 * Returns an empty array if Cosmos is not configured or the query fails.
 */
export async function listAuditEvents(options: {
    userId?: string;
    event?: string;
    sinceMs?: number;
    limit?: number;
}): Promise<AuditEventDocument[]> {
    if (!isCosmosConfigured()) {
        return [];
    }

    const { userId, event, sinceMs, limit = 100 } = options;

    const conditions: string[] = ['c.productId = @productId'];
    const parameters: Array<{ name: string; value: string | number | boolean | null }> = [
        { name: '@productId', value: config.PRODUCT_ID },
    ];

    if (userId) {
        conditions.push('c.userId = @userId');
        parameters.push({ name: '@userId', value: userId });
    }
    if (event) {
        conditions.push('c.event = @event');
        parameters.push({ name: '@event', value: event });
    }
    if (sinceMs !== undefined) {
        conditions.push('c.tsMs >= @sinceMs');
        parameters.push({ name: '@sinceMs', value: sinceMs });
    }

    const query = `SELECT TOP ${Math.min(limit, 500)} * FROM c WHERE ${conditions.join(' AND ')} ORDER BY c.tsMs DESC`;

    try {
        const container = getContainer(CONTAINER_NAME);
        const { resources } = await container.items
            .query<AuditEventDocument>({ query, parameters })
            .fetchAll();
        return resources;
    } catch (error) {
        logger.warn(`[Audit] Cosmos query failed: ${error instanceof Error ? error.message : 'unknown error'}`);
        return [];
    }
}
