/**
 * WebhookSubscriptionController - Subscription REST API
 *
 * REST endpoints for managing webhook subscriptions.
 *
 * @module Webhooks
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  WebhookSubscriptionService,
  CreateSubscriptionDto,
  UpdateSubscriptionDto,
  SubscriptionFilters,
  BulkSubscribeDto,
} from '../services/subscription.service';
import { AuthMiddleware } from '../../auth/middleware/auth.middleware';
import { AuthService } from '../../auth/services/auth.service';
import { WebhookSubscription } from '../entities/subscription.entity';
import { WebhookEndpoint } from '../entities/endpoint.entity';
import { WebhookEventType } from '../entities/event-type.entity';
import { User } from '../../core/entities/user.entity';
import { Tenant } from '../../core/entities/tenant.entity';
import { RefreshToken } from '../../auth/entities/refresh-token.entity';
import { ServiceContext } from '../../../shared/services/base.service';

export function createWebhookSubscriptionController(dataSource: DataSource): Router {
  const router = Router();

  // Repositories
  const subscriptionRepository = dataSource.getRepository(WebhookSubscription);
  const endpointRepository = dataSource.getRepository(WebhookEndpoint);
  const eventTypeRepository = dataSource.getRepository(WebhookEventType);
  const userRepository = dataSource.getRepository(User);
  const tenantRepository = dataSource.getRepository(Tenant);
  const refreshTokenRepository = dataSource.getRepository(RefreshToken);

  // Services
  const subscriptionService = new WebhookSubscriptionService(
    subscriptionRepository,
    endpointRepository,
    eventTypeRepository
  );
  const authService = new AuthService(userRepository, tenantRepository, refreshTokenRepository as any);
  const authMiddleware = new AuthMiddleware(authService, dataSource);

  // Helper to create context
  const getContext = (req: Request): ServiceContext => {
    if (!req.tenantId) {
      throw new Error('Tenant ID is required');
    }
    return {
      tenantId: req.tenantId,
      userId: req.user?.sub,
    };
  };

  /**
   * GET /webhooks/subscriptions
   * List all subscriptions
   */
  router.get('/', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const filters: SubscriptionFilters = {
        page: parseInt(req.query.page as string) || 1,
        limit: Math.min(parseInt(req.query.limit as string) || 50, 200),
      };

      if (req.query.endpointId) {
        filters.endpointId = req.query.endpointId as string;
      }
      if (req.query.eventTypeId) {
        filters.eventTypeId = req.query.eventTypeId as string;
      }
      if (req.query.isActive !== undefined) {
        filters.isActive = req.query.isActive === 'true';
      }

      const result = await subscriptionService.findAll(getContext(req), filters);

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

  /**
   * GET /webhooks/subscriptions/by-endpoint/:endpointId
   * Get subscriptions for a specific endpoint
   */
  router.get('/by-endpoint/:endpointId', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const subscriptions = await subscriptionService.findByEndpointId(
        getContext(req),
        req.params.endpointId
      );
      res.status(200).json({ success: true, data: subscriptions });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /webhooks/subscriptions/by-event-type/:eventTypeId
   * Get subscriptions for a specific event type
   */
  router.get('/by-event-type/:eventTypeId', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const subscriptions = await subscriptionService.findByEventTypeId(
        getContext(req),
        req.params.eventTypeId
      );
      res.status(200).json({ success: true, data: subscriptions });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /webhooks/subscriptions/check
   * Check if endpoint is subscribed to event type
   */
  router.get('/check', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const { endpointId, eventTypeId } = req.query;
      if (!endpointId || !eventTypeId) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'endpointId and eventTypeId are required',
        });
        return;
      }

      const isSubscribed = await subscriptionService.isSubscribed(
        getContext(req),
        endpointId as string,
        eventTypeId as string
      );

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

  /**
   * GET /webhooks/subscriptions/:id
   * Get subscription by ID
   */
  router.get('/:id', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const subscription = await subscriptionService.findById(getContext(req), req.params.id);
      if (!subscription) {
        res.status(404).json({ error: 'Not Found', message: 'Subscription not found' });
        return;
      }

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

  /**
   * POST /webhooks/subscriptions
   * Create new subscription
   */
  router.post('/', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const dto: CreateSubscriptionDto = req.body;

      if (!dto.endpointId || !dto.eventTypeId) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'endpointId and eventTypeId are required',
        });
        return;
      }

      const subscription = await subscriptionService.create(getContext(req), dto);
      res.status(201).json({ success: true, data: subscription });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message.includes('not found')) {
          res.status(404).json({ error: 'Not Found', message: error.message });
          return;
        }
        if (error.message.includes('already exists')) {
          res.status(409).json({ error: 'Conflict', message: error.message });
          return;
        }
      }
      next(error);
    }
  });

  /**
   * POST /webhooks/subscriptions/bulk
   * Bulk subscribe endpoint to multiple event types
   */
  router.post('/bulk', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const dto: BulkSubscribeDto = req.body;

      if (!dto.endpointId || !dto.eventTypeIds || !Array.isArray(dto.eventTypeIds)) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'endpointId and eventTypeIds array are required',
        });
        return;
      }

      const subscriptions = await subscriptionService.bulkSubscribe(getContext(req), dto);
      res.status(201).json({
        success: true,
        data: subscriptions,
        message: `Created ${subscriptions.length} subscriptions`,
      });
    } catch (error) {
      if (error instanceof Error && error.message.includes('not found')) {
        res.status(404).json({ error: 'Not Found', message: error.message });
        return;
      }
      next(error);
    }
  });

  /**
   * PUT /webhooks/subscriptions/:id
   * Update subscription
   */
  router.put('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const dto: UpdateSubscriptionDto = req.body;
      const subscription = await subscriptionService.update(getContext(req), req.params.id, dto);

      if (!subscription) {
        res.status(404).json({ error: 'Not Found', message: 'Subscription not found' });
        return;
      }

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

  /**
   * PATCH /webhooks/subscriptions/:id
   * Partial update subscription
   */
  router.patch('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const dto: UpdateSubscriptionDto = req.body;
      const subscription = await subscriptionService.update(getContext(req), req.params.id, dto);

      if (!subscription) {
        res.status(404).json({ error: 'Not Found', message: 'Subscription not found' });
        return;
      }

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

  /**
   * POST /webhooks/subscriptions/:id/activate
   * Activate subscription
   */
  router.post('/:id/activate', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const subscription = await subscriptionService.activate(getContext(req), req.params.id);
      if (!subscription) {
        res.status(404).json({ error: 'Not Found', message: 'Subscription not found' });
        return;
      }

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

  /**
   * POST /webhooks/subscriptions/:id/deactivate
   * Deactivate subscription
   */
  router.post('/:id/deactivate', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const subscription = await subscriptionService.deactivate(getContext(req), req.params.id);
      if (!subscription) {
        res.status(404).json({ error: 'Not Found', message: 'Subscription not found' });
        return;
      }

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

  /**
   * DELETE /webhooks/subscriptions/:id
   * Delete subscription
   */
  router.delete('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const deleted = await subscriptionService.delete(getContext(req), req.params.id);
      if (!deleted) {
        res.status(404).json({ error: 'Not Found', message: 'Subscription not found' });
        return;
      }

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

  /**
   * DELETE /webhooks/subscriptions/by-endpoint/:endpointId
   * Unsubscribe endpoint from all event types
   */
  router.delete('/by-endpoint/:endpointId', authMiddleware.authenticate, authMiddleware.authorize('admin', 'super_admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const count = await subscriptionService.unsubscribeAll(
        getContext(req),
        req.params.endpointId
      );

      res.status(200).json({
        success: true,
        message: `Removed ${count} subscriptions`,
      });
    } catch (error) {
      next(error);
    }
  });

  return router;
}

export default createWebhookSubscriptionController;
