// src/routes/budget.ts
import express, { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import passport from './passport.routes';
import { budgetRepo } from '../services/db/index.db';
import type { UserProfile } from '../types';
import { validateRequest } from '../middleware/validation.middleware';

const router = express.Router();

// Helper for consistent required string validation (handles missing/null/empty)
const requiredString = (message: string) => z.preprocess((val) => val ?? '', z.string().min(1, message));

// --- Zod Schemas for Budget Routes (as per ADR-003) ---

const budgetIdParamSchema = z.object({
  params: z.object({
    id: z.coerce.number().int().positive("Invalid ID for parameter 'id'. Must be a number."),
  }),
});

const createBudgetSchema = z.object({
  body: z.object({
    name: requiredString('Budget name is required.'),
    amount_cents: z.number().int().positive('Amount must be a positive integer.'),
    period: z.enum(['weekly', 'monthly']),
    start_date: z.string().date('Start date must be a valid date in YYYY-MM-DD format.'),
  }),
});

const updateBudgetSchema = budgetIdParamSchema.extend({
  body: createBudgetSchema.shape.body.partial().refine(data => Object.keys(data).length > 0, {
    message: 'At least one field to update must be provided.',
  }),
});

const spendingAnalysisSchema = z.object({
  query: z.object({
    startDate: z.string().date('startDate must be a valid date in YYYY-MM-DD format.'),
    endDate: z.string().date('endDate must be a valid date in YYYY-MM-DD format.'),
  }),
});

// Middleware to ensure user is authenticated for all budget routes
router.use(passport.authenticate('jwt', { session: false }));

/**
 * GET /api/budgets - Get all budgets for the authenticated user.
 */
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
  const user = req.user as UserProfile;
  try {
    const budgets = await budgetRepo.getBudgetsForUser(user.user_id, req.log);
    res.json(budgets);
  } catch (error) {
    req.log.error({ error, userId: user.user_id }, 'Error fetching budgets');
    next(error);
  }
});

/**
 * POST /api/budgets - Create a new budget for the authenticated user.
 */
router.post('/', validateRequest(createBudgetSchema), async (req: Request, res: Response, next: NextFunction) => {
  const user = req.user as UserProfile;
  type CreateBudgetRequest = z.infer<typeof createBudgetSchema>;
  const { body } = req as unknown as CreateBudgetRequest;
  try {
    const newBudget = await budgetRepo.createBudget(user.user_id, body, req.log);
    res.status(201).json(newBudget);
  } catch (error: unknown) {
    req.log.error({ error, userId: user.user_id, body }, 'Error creating budget');
    next(error);
  }
});

/**
 * PUT /api/budgets/:id - Update an existing budget.
 */
router.put('/:id', validateRequest(updateBudgetSchema), async (req: Request, res: Response, next: NextFunction) => {
  const user = req.user as UserProfile;
  type UpdateBudgetRequest = z.infer<typeof updateBudgetSchema>;
  const { params, body } = req as unknown as UpdateBudgetRequest;
  try {
    const updatedBudget = await budgetRepo.updateBudget(params.id, user.user_id, body, req.log);
    res.json(updatedBudget);
  } catch (error: unknown) {
    req.log.error({ error, userId: user.user_id, budgetId: params.id }, 'Error updating budget');
    next(error);
  }
});

/**
 * DELETE /api/budgets/:id - Delete a budget.
 */
router.delete('/:id', validateRequest(budgetIdParamSchema), async (req: Request, res: Response, next: NextFunction) => {
  const user = req.user as UserProfile;
  type DeleteBudgetRequest = z.infer<typeof budgetIdParamSchema>;
  const { params } = req as unknown as DeleteBudgetRequest;
  try {
    await budgetRepo.deleteBudget(params.id, user.user_id, req.log);
    res.status(204).send(); // No Content
  } catch (error: unknown) {
    req.log.error({ error, userId: user.user_id, budgetId: params.id }, 'Error deleting budget');
    next(error);
  }
});

/**
 * GET /api/spending-analysis - Get spending breakdown by category for a date range.
 * Query params: startDate (YYYY-MM-DD), endDate (YYYY-MM-DD)
 */
router.get('/spending-analysis', validateRequest(spendingAnalysisSchema), async (req: Request, res: Response, next: NextFunction) => {
  const user = req.user as UserProfile;
  type SpendingAnalysisRequest = z.infer<typeof spendingAnalysisSchema>;
  const { query: { startDate, endDate } } = req as unknown as SpendingAnalysisRequest;

  try {
    const spendingData = await budgetRepo.getSpendingByCategory(user.user_id, startDate, endDate, req.log);
    res.json(spendingData);
  } catch (error) {
    req.log.error({ error, userId: user.user_id, startDate, endDate }, 'Error fetching spending analysis');
    next(error);
  }
});

export default router;