"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.videoTrackingRoutes = videoTrackingRoutes;
const auth_1 = require("../middleware/auth");
const video_analytics_service_1 = require("../services/video-analytics.service");
const logger_1 = require("../../../utils/logger");
const zod_1 = require("zod");
// Validation schemas
const recordViewSchema = zod_1.z.object({
    videoId: zod_1.z.number(),
    referer: zod_1.z.string().optional(),
});
const recordEventSchema = zod_1.z.object({
    videoId: zod_1.z.number(),
    viewId: zod_1.z.number().optional(),
    eventType: zod_1.z.enum(['play', 'pause', 'seek', 'complete']),
    timestamp: zod_1.z.number().min(0),
});
const updateWatchTimeSchema = zod_1.z.object({
    viewId: zod_1.z.number(),
    watchTimeSeconds: zod_1.z.number().min(0),
});
async function videoTrackingRoutes(fastify) {
    /**
     * POST /track/view
     * Record a new video view (called when video starts loading)
     * Public endpoint - no auth required, but optionally uses auth if available
     */
    fastify.post('/view', {
        preHandler: auth_1.optionalAuth,
    }, async (request, reply) => {
        const { videoId, referer } = request.body;
        // Validate input
        try {
            recordViewSchema.parse(request.body);
        }
        catch (error) {
            return reply.code(400).send({ message: 'Invalid input', error });
        }
        try {
            // Get user ID if authenticated
            const userId = request.user?.id;
            // Get IP address and user agent from request
            const ipAddress = request.ip;
            const userAgent = request.headers['user-agent'];
            // Record the view
            const viewId = await video_analytics_service_1.videoAnalyticsService.recordView({
                videoId,
                userId,
                ipAddress,
                userAgent,
                referer,
            });
            return {
                success: true,
                viewId,
            };
        }
        catch (error) {
            logger_1.logger.error('Failed to record view', { error, videoId });
            // Don't fail the request - we don't want analytics failures to break playback
            return {
                success: false,
                viewId: null,
            };
        }
    });
    /**
     * POST /track/event
     * Record a video event (play, pause, seek, complete)
     * Public endpoint - no auth required
     */
    fastify.post('/event', async (request, reply) => {
        const { videoId, viewId, eventType, timestamp } = request.body;
        // Validate input
        try {
            recordEventSchema.parse(request.body);
        }
        catch (error) {
            return reply.code(400).send({ message: 'Invalid input', error });
        }
        try {
            await video_analytics_service_1.videoAnalyticsService.recordEvent({
                videoId,
                viewId,
                eventType,
                timestamp,
            });
            return {
                success: true,
            };
        }
        catch (error) {
            logger_1.logger.error('Failed to record event', { error, videoId, eventType });
            // Don't fail the request - analytics failures shouldn't break playback
            return {
                success: false,
            };
        }
    });
    /**
     * POST /track/heartbeat
     * Update watch time for a view (called every 10 seconds during playback)
     * Public endpoint - no auth required
     */
    fastify.post('/heartbeat', async (request, reply) => {
        const { viewId, watchTimeSeconds } = request.body;
        // Validate input
        try {
            updateWatchTimeSchema.parse(request.body);
        }
        catch (error) {
            return reply.code(400).send({ message: 'Invalid input', error });
        }
        try {
            await video_analytics_service_1.videoAnalyticsService.updateWatchTime(viewId, watchTimeSeconds);
            return {
                success: true,
            };
        }
        catch (error) {
            logger_1.logger.error('Failed to update watch time', { error, viewId });
            // Don't fail the request
            return {
                success: false,
            };
        }
    });
    /**
     * POST /track/batch
     * Batch record multiple events (useful for reducing requests)
     * Public endpoint - no auth required
     */
    fastify.post('/batch', async (request, reply) => {
        const { events } = request.body;
        if (!Array.isArray(events) || events.length === 0) {
            return reply.code(400).send({ message: 'Events array is required' });
        }
        if (events.length > 50) {
            return reply.code(400).send({ message: 'Maximum 50 events per batch' });
        }
        try {
            // Validate each event's data against per-type schemas before processing
            for (const event of events) {
                switch (event.type) {
                    case 'view':
                        recordViewSchema.parse(event.data);
                        break;
                    case 'event':
                        recordEventSchema.parse(event.data);
                        break;
                    case 'heartbeat':
                        updateWatchTimeSchema.parse(event.data);
                        break;
                    default:
                        return reply.code(400).send({ message: `Unknown event type: ${event.type}` });
                }
            }
            const results = await Promise.allSettled(events.map(async (event) => {
                switch (event.type) {
                    case 'view':
                        return video_analytics_service_1.videoAnalyticsService.recordView(event.data);
                    case 'event':
                        return video_analytics_service_1.videoAnalyticsService.recordEvent(event.data);
                    case 'heartbeat':
                        return video_analytics_service_1.videoAnalyticsService.updateWatchTime(event.data.viewId, event.data.watchTimeSeconds);
                    default:
                        throw new Error(`Unknown event type: ${event.type}`);
                }
            }));
            const successful = results.filter((r) => r.status === 'fulfilled').length;
            const failed = results.filter((r) => r.status === 'rejected').length;
            return {
                success: true,
                total: events.length,
                successful,
                failed,
            };
        }
        catch (error) {
            logger_1.logger.error('Failed to process batch tracking', { error });
            return reply.code(500).send({ message: 'Failed to process batch' });
        }
    });
    /**
     * GET /track/health
     * Health check for tracking endpoints
     */
    fastify.get('/health', async (request, reply) => {
        return {
            status: 'ok',
            service: 'video-tracking',
            timestamp: new Date().toISOString(),
        };
    });
}
//# sourceMappingURL=video-tracking.routes.js.map