import { Request, Response, NextFunction, Router } from 'express';
import { NotificationsService } from '../services/notifications.service';

export class NotificationsController {
  public router: Router;

  constructor(private readonly notificationsService: NotificationsService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Channels
    this.router.get('/channels', this.findAllChannels.bind(this));
    this.router.get('/channels/:code', this.findChannelByCode.bind(this));

    // Templates
    this.router.get('/templates', this.findAllTemplates.bind(this));
    this.router.get('/templates/:code', this.findTemplateByCode.bind(this));
    this.router.post('/templates', this.createTemplate.bind(this));

    // Preferences
    this.router.get('/preferences', this.getPreferences.bind(this));
    this.router.patch('/preferences', this.updatePreferences.bind(this));

    // Notifications
    this.router.post('/', this.createNotification.bind(this));
    this.router.get('/pending', this.findPendingNotifications.bind(this));
    this.router.patch('/:id/status', this.updateNotificationStatus.bind(this));

    // In-App Notifications
    this.router.get('/in-app', this.findInAppNotifications.bind(this));
    this.router.get('/in-app/unread-count', this.getUnreadCount.bind(this));
    this.router.post('/in-app/:id/read', this.markAsRead.bind(this));
    this.router.post('/in-app/read-all', this.markAllAsRead.bind(this));
    this.router.post('/in-app', this.createInAppNotification.bind(this));
  }

  // ============================================
  // CHANNELS
  // ============================================

  private async findAllChannels(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const channels = await this.notificationsService.findAllChannels();
      res.json({ data: channels });
    } catch (error) {
      next(error);
    }
  }

  private async findChannelByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { code } = req.params;
      const channel = await this.notificationsService.findChannelByCode(code);

      if (!channel) {
        res.status(404).json({ error: 'Channel not found' });
        return;
      }

      res.json({ data: channel });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // TEMPLATES
  // ============================================

  private async findAllTemplates(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const templates = await this.notificationsService.findAllTemplates(tenantId);
      res.json({ data: templates, total: templates.length });
    } catch (error) {
      next(error);
    }
  }

  private async findTemplateByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { code } = req.params;
      const tenantId = req.headers['x-tenant-id'] as string;
      const channelType = req.query.channelType as string;

      if (!channelType) {
        res.status(400).json({ error: 'channelType query parameter is required' });
        return;
      }

      const template = await this.notificationsService.findTemplateByCode(code, channelType, tenantId);

      if (!template) {
        res.status(404).json({ error: 'Template not found' });
        return;
      }

      res.json({ data: template });
    } catch (error) {
      next(error);
    }
  }

  private async createTemplate(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const template = await this.notificationsService.createTemplate(tenantId, req.body, userId);
      res.status(201).json({ data: template });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // PREFERENCES
  // ============================================

  private async getPreferences(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const preferences = await this.notificationsService.getPreferences(userId, tenantId);
      res.json({ data: preferences });
    } catch (error) {
      next(error);
    }
  }

  private async updatePreferences(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const preferences = await this.notificationsService.updatePreferences(userId, tenantId, req.body);
      res.json({ data: preferences });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // NOTIFICATIONS
  // ============================================

  private async createNotification(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;

      const notification = await this.notificationsService.createNotification(tenantId, req.body);
      res.status(201).json({ data: notification });
    } catch (error) {
      next(error);
    }
  }

  private async findPendingNotifications(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const limit = parseInt(req.query.limit as string) || 100;
      const notifications = await this.notificationsService.findPendingNotifications(limit);
      res.json({ data: notifications, total: notifications.length });
    } catch (error) {
      next(error);
    }
  }

  private async updateNotificationStatus(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const { status, errorMessage } = req.body;

      const notification = await this.notificationsService.updateNotificationStatus(id, status, errorMessage);

      if (!notification) {
        res.status(404).json({ error: 'Notification not found' });
        return;
      }

      res.json({ data: notification });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // IN-APP NOTIFICATIONS
  // ============================================

  private async findInAppNotifications(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;
      const includeRead = req.query.includeRead === 'true';

      const notifications = await this.notificationsService.findInAppNotifications(userId, tenantId, includeRead);
      res.json({ data: notifications, total: notifications.length });
    } catch (error) {
      next(error);
    }
  }

  private async getUnreadCount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const count = await this.notificationsService.getUnreadCount(userId, tenantId);
      res.json({ data: { unreadCount: count } });
    } catch (error) {
      next(error);
    }
  }

  private async markAsRead(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const marked = await this.notificationsService.markAsRead(id);

      if (!marked) {
        res.status(404).json({ error: 'Notification not found or already read' });
        return;
      }

      res.json({ data: { success: true } });
    } catch (error) {
      next(error);
    }
  }

  private async markAllAsRead(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const count = await this.notificationsService.markAllAsRead(userId, tenantId);
      res.json({ data: { markedCount: count } });
    } catch (error) {
      next(error);
    }
  }

  private async createInAppNotification(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const { userId, ...data } = req.body;

      const notification = await this.notificationsService.createInAppNotification(tenantId, userId, data);
      res.status(201).json({ data: notification });
    } catch (error) {
      next(error);
    }
  }
}
