/**
 * Preference Controller
 * REST endpoints for user notification preferences.
 *
 * @module Notifications
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  PreferenceService,
  UpdatePreferenceDto,
  ChannelPreferences,
} from '../services/preference.service';
import { AuthMiddleware } from '../../auth/middleware/auth.middleware';
import { AuthService } from '../../auth/services/auth.service';
import { User } from '../../core/entities/user.entity';
import { Tenant } from '../../core/entities/tenant.entity';
import { RefreshToken } from '../../auth/entities/refresh-token.entity';
import { NotificationPreference, DigestFrequency } from '../entities/preference.entity';

/**
 * Create preference controller router
 */
export function createPreferenceController(dataSource: DataSource): Router {
  const router = Router();

  // Repositories
  const userRepository = dataSource.getRepository(User);
  const tenantRepository = dataSource.getRepository(Tenant);
  const refreshTokenRepository = dataSource.getRepository(RefreshToken);
  const preferenceRepository = dataSource.getRepository(NotificationPreference);

  // Services
  const authService = new AuthService(userRepository, tenantRepository, refreshTokenRepository as any);
  const authMiddleware = new AuthMiddleware(authService, dataSource);
  const preferenceService = new PreferenceService(preferenceRepository);

  // Helper to get service context
  const getContext = (req: Request) => ({
    tenantId: req.tenantId!,
    userId: req.user?.sub,
  });

  /**
   * GET /notifications/preferences/me
   * Get current user's notification preferences
   */
  router.get(
    '/me',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const preferences = await preferenceService.getOrCreate(ctx, ctx.userId);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/me
   * Update current user's notification preferences
   */
  router.patch(
    '/me',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const dto: UpdatePreferenceDto = req.body;
        const preferences = await preferenceService.upsert(ctx, ctx.userId, dto);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * GET /notifications/preferences/me/channels
   * Get current user's channel preferences
   */
  router.get(
    '/me/channels',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const channels = await preferenceService.getChannelPreferences(ctx, ctx.userId);

        res.status(200).json({ success: true, data: channels });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/me/channels
   * Update current user's channel preferences
   */
  router.patch(
    '/me/channels',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const channels: Partial<ChannelPreferences> = req.body;
        const preferences = await preferenceService.updateChannelPreferences(ctx, ctx.userId, channels);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * POST /notifications/preferences/me/enable-all
   * Enable all notifications for current user
   */
  router.post(
    '/me/enable-all',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const preferences = await preferenceService.enableAll(ctx, ctx.userId);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * POST /notifications/preferences/me/disable-all
   * Disable all notifications for current user
   */
  router.post(
    '/me/disable-all',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const preferences = await preferenceService.disableAll(ctx, ctx.userId);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/me/quiet-hours
   * Set quiet hours for current user
   */
  router.patch(
    '/me/quiet-hours',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const { start, end } = req.body;
        const preferences = await preferenceService.setQuietHours(ctx, ctx.userId, start, end);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * DELETE /notifications/preferences/me/quiet-hours
   * Clear quiet hours for current user
   */
  router.delete(
    '/me/quiet-hours',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const preferences = await preferenceService.clearQuietHours(ctx, ctx.userId);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/me/digest
   * Set digest frequency for current user
   */
  router.patch(
    '/me/digest',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const { frequency, day, hour } = req.body;
        if (!frequency) {
          res.status(400).json({ error: 'Bad Request', message: 'frequency is required' });
          return;
        }

        const validFrequencies: DigestFrequency[] = ['instant', 'hourly', 'daily', 'weekly'];
        if (!validFrequencies.includes(frequency)) {
          res.status(400).json({
            error: 'Bad Request',
            message: `Invalid frequency. Must be one of: ${validFrequencies.join(', ')}`,
          });
          return;
        }

        const preferences = await preferenceService.setDigestFrequency(
          ctx,
          ctx.userId,
          frequency,
          { day, hour },
        );

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/me/timezone
   * Set timezone for current user
   */
  router.patch(
    '/me/timezone',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const { timezone } = req.body;
        if (!timezone) {
          res.status(400).json({ error: 'Bad Request', message: 'timezone is required' });
          return;
        }

        const preferences = await preferenceService.setTimezone(ctx, ctx.userId, timezone);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/me/categories
   * Update category preferences for current user
   */
  router.patch(
    '/me/categories',
    authMiddleware.authenticate,
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId || !ctx.userId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID and User ID required' });
          return;
        }

        const categoryPreferences = req.body;
        const preferences = await preferenceService.updateCategoryPreferences(
          ctx,
          ctx.userId,
          categoryPreferences,
        );

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  // ============= Admin Routes =============

  /**
   * GET /notifications/preferences/users/:userId
   * Get preferences for a specific user (admin only)
   */
  router.get(
    '/users/:userId',
    authMiddleware.authenticate,
    authMiddleware.authorize('admin', 'super_admin'),
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
          return;
        }

        const preferences = await preferenceService.findByUserId(ctx, req.params.userId);
        if (!preferences) {
          res.status(404).json({ error: 'Not Found', message: 'Preferences not found' });
          return;
        }

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * PATCH /notifications/preferences/users/:userId
   * Update preferences for a specific user (admin only)
   */
  router.patch(
    '/users/:userId',
    authMiddleware.authenticate,
    authMiddleware.authorize('admin', 'super_admin'),
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
          return;
        }

        const dto: UpdatePreferenceDto = req.body;
        const preferences = await preferenceService.upsert(ctx, req.params.userId, dto);

        res.status(200).json({ success: true, data: preferences });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * DELETE /notifications/preferences/users/:userId
   * Delete preferences for a specific user (admin only)
   */
  router.delete(
    '/users/:userId',
    authMiddleware.authenticate,
    authMiddleware.authorize('admin', 'super_admin'),
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
          return;
        }

        const deleted = await preferenceService.delete(ctx, req.params.userId);
        if (!deleted) {
          res.status(404).json({ error: 'Not Found', message: 'Preferences not found' });
          return;
        }

        res.status(200).json({ success: true, message: 'Preferences deleted' });
      } catch (error) {
        next(error);
      }
    },
  );

  /**
   * GET /notifications/preferences
   * List all preferences for the tenant (admin only)
   */
  router.get(
    '/',
    authMiddleware.authenticate,
    authMiddleware.authorize('admin', 'super_admin'),
    async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        const ctx = getContext(req);
        if (!ctx.tenantId) {
          res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
          return;
        }

        const page = parseInt(req.query.page as string) || 1;
        const limit = Math.min(parseInt(req.query.limit as string) || 50, 100);

        const result = await preferenceService.findAllByTenant(ctx, { page, limit });

        res.status(200).json({
          success: true,
          data: result.data,
          pagination: {
            page,
            limit,
            total: result.total,
            totalPages: Math.ceil(result.total / limit),
          },
        });
      } catch (error) {
        next(error);
      }
    },
  );

  return router;
}

export default createPreferenceController;
