// src/routes/flyer.routes.ts
import { Router } from 'express';
import * as db from '../services/db/index.db';
import { z } from 'zod';
import { validateRequest } from '../middleware/validation.middleware';
import { optionalNumeric } from '../utils/zodUtils';
import { publicReadLimiter, batchLimiter, trackingLimiter } from '../config/rateLimiters';
import { sendSuccess } from '../utils/apiResponse';

const router = Router();

// --- Zod Schemas for Flyer Routes ---

const getFlyersSchema = z.object({
  query: z.object({
    limit: optionalNumeric({ default: 20, integer: true, positive: true }),
    offset: optionalNumeric({ default: 0, integer: true, nonnegative: true }),
  }),
});

const flyerIdParamSchema = z.object({
  params: z.object({
    id: z.coerce.number().int('Invalid flyer ID provided.').positive('Invalid flyer ID provided.'),
  }),
});

const batchFetchSchema = z.object({
  body: z.object({
    flyerIds: z.array(z.number().int().positive()).min(1, 'flyerIds must be a non-empty array.'),
  }),
});

const batchCountSchema = z.object({
  body: z.object({
    flyerIds: z.array(z.number().int().positive()),
  }),
});

const trackItemSchema = z.object({
  params: z.object({
    itemId: z.coerce.number().int().positive('Invalid item ID provided.'),
  }),
  body: z.object({
    type: z.enum(['view', 'click'], {
      message: 'A valid interaction type ("view" or "click") is required.',
    }),
  }),
});

/**
 * @openapi
 * /flyers:
 *   get:
 *     summary: Get all flyers
 *     description: Returns a paginated list of all flyers.
 *     tags:
 *       - Flyers
 *     parameters:
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 20
 *         description: Maximum number of flyers to return
 *       - in: query
 *         name: offset
 *         schema:
 *           type: integer
 *           default: 0
 *         description: Number of flyers to skip
 *     responses:
 *       200:
 *         description: List of flyers
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       flyer_id:
 *                         type: integer
 *                       store_id:
 *                         type: integer
 *                       flyer_name:
 *                         type: string
 *                       start_date:
 *                         type: string
 *                         format: date
 *                       end_date:
 *                         type: string
 *                         format: date
 */
router.get(
  '/',
  publicReadLimiter,
  validateRequest(getFlyersSchema),
  async (req, res, next): Promise<void> => {
    try {
      // The `validateRequest` middleware ensures `req.query` is valid.
      // We parse it here to apply Zod's coercions (string to number) and defaults.
      const { limit, offset } = getFlyersSchema.shape.query.parse(req.query);

      const flyers = await db.flyerRepo.getFlyers(req.log, limit, offset);
      sendSuccess(res, flyers);
    } catch (error) {
      req.log.error({ error }, 'Error fetching flyers in /api/flyers:');
      next(error);
    }
  },
);

/**
 * @openapi
 * /flyers/{id}:
 *   get:
 *     summary: Get flyer by ID
 *     description: Returns a single flyer by its ID.
 *     tags:
 *       - Flyers
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: The flyer ID
 *     responses:
 *       200:
 *         description: Flyer details
 *       404:
 *         description: Flyer not found
 */
router.get(
  '/:id',
  publicReadLimiter,
  validateRequest(flyerIdParamSchema),
  async (req, res, next): Promise<void> => {
    try {
      // Explicitly parse to get the coerced number type for `id`.
      const { id } = flyerIdParamSchema.shape.params.parse(req.params);
      const flyer = await db.flyerRepo.getFlyerById(id);
      sendSuccess(res, flyer);
    } catch (error) {
      req.log.error({ error, flyerId: req.params.id }, 'Error fetching flyer by ID:');
      next(error);
    }
  },
);

/**
 * @openapi
 * /flyers/{id}/items:
 *   get:
 *     summary: Get flyer items
 *     description: Returns all items (deals) for a specific flyer.
 *     tags:
 *       - Flyers
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: The flyer ID
 *     responses:
 *       200:
 *         description: List of flyer items
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       item_id:
 *                         type: integer
 *                       item_name:
 *                         type: string
 *                       price:
 *                         type: number
 *                       unit:
 *                         type: string
 */
router.get(
  '/:id/items',
  publicReadLimiter,
  validateRequest(flyerIdParamSchema),
  async (req, res, next): Promise<void> => {
    try {
      // Explicitly parse to get the coerced number type for `id`.
      const { id } = flyerIdParamSchema.shape.params.parse(req.params);
      const items = await db.flyerRepo.getFlyerItems(id, req.log);
      sendSuccess(res, items);
    } catch (error) {
      req.log.error(
        { error, flyerId: req.params.id },
        'Error fetching flyer items in /api/flyers/:id/items:',
      );
      next(error);
    }
  },
);

/**
 * @openapi
 * /flyers/items/batch-fetch:
 *   post:
 *     summary: Batch fetch flyer items
 *     description: Returns all items for multiple flyers in a single request.
 *     tags:
 *       - Flyers
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - flyerIds
 *             properties:
 *               flyerIds:
 *                 type: array
 *                 items:
 *                   type: integer
 *                 minItems: 1
 *                 example: [1, 2, 3]
 *     responses:
 *       200:
 *         description: Items for all requested flyers
 */
type BatchFetchRequest = z.infer<typeof batchFetchSchema>;
router.post(
  '/items/batch-fetch',
  batchLimiter,
  validateRequest(batchFetchSchema),
  async (req, res, next): Promise<void> => {
    const { body } = req as unknown as BatchFetchRequest;
    try {
      // No re-parsing needed here as `validateRequest` has already ensured the body shape,
      // and `express.json()` has parsed it. There's no type coercion to apply.
      const items = await db.flyerRepo.getFlyerItemsForFlyers(body.flyerIds, req.log);
      sendSuccess(res, items);
    } catch (error) {
      req.log.error({ error }, 'Error fetching batch flyer items');
      next(error);
    }
  },
);

/**
 * @openapi
 * /flyers/items/batch-count:
 *   post:
 *     summary: Batch count flyer items
 *     description: Returns the total item count for multiple flyers.
 *     tags:
 *       - Flyers
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - flyerIds
 *             properties:
 *               flyerIds:
 *                 type: array
 *                 items:
 *                   type: integer
 *                 example: [1, 2, 3]
 *     responses:
 *       200:
 *         description: Total item count
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: object
 *                   properties:
 *                     count:
 *                       type: integer
 *                       example: 42
 */
type BatchCountRequest = z.infer<typeof batchCountSchema>;
router.post(
  '/items/batch-count',
  batchLimiter,
  validateRequest(batchCountSchema),
  async (req, res, next): Promise<void> => {
    const { body } = req as unknown as BatchCountRequest;
    try {
      // The schema ensures flyerIds is an array of numbers.
      // The `?? []` was redundant as `validateRequest` would have already caught a missing `flyerIds`.
      const count = await db.flyerRepo.countFlyerItemsForFlyers(body.flyerIds, req.log);
      sendSuccess(res, { count });
    } catch (error) {
      req.log.error({ error }, 'Error counting batch flyer items');
      next(error);
    }
  },
);

/**
 * @openapi
 * /flyers/items/{itemId}/track:
 *   post:
 *     summary: Track item interaction
 *     description: Records a view or click interaction with a flyer item for analytics.
 *     tags:
 *       - Flyers
 *     parameters:
 *       - in: path
 *         name: itemId
 *         required: true
 *         schema:
 *           type: integer
 *         description: The flyer item ID
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - type
 *             properties:
 *               type:
 *                 type: string
 *                 enum: [view, click]
 *                 description: Type of interaction
 *     responses:
 *       202:
 *         description: Tracking accepted (fire-and-forget)
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: object
 *                   properties:
 *                     message:
 *                       type: string
 *                       example: Tracking accepted
 */
router.post(
  '/items/:itemId/track',
  trackingLimiter,
  validateRequest(trackItemSchema),
  (req, res, next): void => {
    try {
      // Explicitly parse to get coerced types.
      const { params, body } = trackItemSchema.parse({ params: req.params, body: req.body });

      // Fire-and-forget: we don't await the tracking call to avoid delaying the response.
      // We add a .catch to log any potential errors without crashing the server process.
      db.flyerRepo.trackFlyerItemInteraction(params.itemId, body.type, req.log).catch((error) => {
        req.log.error({ error, itemId: params.itemId }, 'Flyer item interaction tracking failed');
      });

      sendSuccess(res, { message: 'Tracking accepted' }, 202);
    } catch (error) {
      // This will catch Zod parsing errors if they occur.
      next(error);
    }
  },
);

export default router;
