/**
 * DashboardController - Controller de Dashboards
 *
 * Endpoints REST para gestión de dashboards y widgets.
 *
 * @module Reports
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  DashboardService,
  CreateDashboardDto,
  UpdateDashboardDto,
  DashboardFilters,
  CreateWidgetDto,
  UpdateWidgetDto,
} from '../services/dashboard.service';
import { AuthMiddleware } from '../../auth/middleware/auth.middleware';
import { AuthService } from '../../auth/services/auth.service';
import { Dashboard } from '../entities/dashboard.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 createDashboardController(dataSource: DataSource): Router {
  const router = Router();

  // Repositorios
  const dashboardRepository = dataSource.getRepository(Dashboard);
  const userRepository = dataSource.getRepository(User);
  const tenantRepository = dataSource.getRepository(Tenant);
  const refreshTokenRepository = dataSource.getRepository(RefreshToken);

  // Servicios
  const dashboardService = new DashboardService(dashboardRepository, dataSource);
  const authService = new AuthService(userRepository, tenantRepository, refreshTokenRepository as any);
  const authMiddleware = new AuthMiddleware(authService, dataSource);

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

  /**
   * GET /dashboards
   * Listar dashboards con filtros
   */
  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 page = parseInt(req.query.page as string) || 1;
      const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);

      const filters: DashboardFilters = {};
      if (req.query.dashboardType) filters.dashboardType = req.query.dashboardType as any;
      if (req.query.visibility) filters.visibility = req.query.visibility as any;
      if (req.query.ownerId) filters.ownerId = req.query.ownerId as string;
      if (req.query.fraccionamientoId) filters.fraccionamientoId = req.query.fraccionamientoId as string;
      if (req.query.isActive !== undefined) filters.isActive = req.query.isActive === 'true';
      if (req.query.search) filters.search = req.query.search as string;

      const result = await dashboardService.findWithFilters(getContext(req), filters, page, limit);

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

  /**
   * GET /dashboards/stats
   * Estadísticas de dashboards
   */
  router.get('/stats', 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 stats = await dashboardService.getStats(getContext(req));
      res.status(200).json({ success: true, data: stats });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /dashboards/my
   * Mis dashboards
   */
  router.get('/my', 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 userId = req.user?.sub;
      if (!userId) {
        res.status(400).json({ error: 'Bad Request', message: 'User ID required' });
        return;
      }

      const dashboards = await dashboardService.findByOwner(getContext(req), userId);
      res.status(200).json({ success: true, data: dashboards });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /dashboards/type/:type
   * Dashboards por tipo
   */
  router.get('/type/:type', 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 dashboards = await dashboardService.findByType(getContext(req), req.params.type as any);
      res.status(200).json({ success: true, data: dashboards });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /dashboards/type/:type/default
   * Dashboard por defecto para un tipo
   */
  router.get('/type/:type/default', 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 dashboard = await dashboardService.findDefault(getContext(req), req.params.type as any);
      if (!dashboard) {
        res.status(404).json({ error: 'Not Found', message: 'No default dashboard found' });
        return;
      }

      // Registrar vista
      await dashboardService.recordView(getContext(req), dashboard.id);

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

  /**
   * GET /dashboards/code/:code
   * Dashboard por código
   */
  router.get('/code/:code', 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 dashboard = await dashboardService.findByCode(getContext(req), req.params.code);
      if (!dashboard) {
        res.status(404).json({ error: 'Not Found', message: 'Dashboard not found' });
        return;
      }

      // Registrar vista
      await dashboardService.recordView(getContext(req), dashboard.id);

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

  /**
   * GET /dashboards/:id
   * Obtener dashboard con widgets
   */
  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 dashboard = await dashboardService.findByIdWithWidgets(getContext(req), req.params.id);
      if (!dashboard) {
        res.status(404).json({ error: 'Not Found', message: 'Dashboard not found' });
        return;
      }

      // Registrar vista
      await dashboardService.recordView(getContext(req), dashboard.id);

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

  /**
   * POST /dashboards
   * Crear dashboard
   */
  router.post('/', 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 dto: CreateDashboardDto = req.body;
      if (!dto.code || !dto.name) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'code and name are required',
        });
        return;
      }

      const dashboard = await dashboardService.createDashboard(getContext(req), dto);
      res.status(201).json({ success: true, data: dashboard });
    } catch (error) {
      if (error instanceof Error && error.message.includes('already exists')) {
        res.status(409).json({ error: 'Conflict', message: error.message });
        return;
      }
      next(error);
    }
  });

  /**
   * PUT /dashboards/:id
   * Actualizar dashboard
   */
  router.put('/: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 dto: UpdateDashboardDto = req.body;
      const dashboard = await dashboardService.update(getContext(req), req.params.id, dto);

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

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

  /**
   * POST /dashboards/:id/set-default
   * Establecer como dashboard por defecto
   */
  router.post('/:id/set-default', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), 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 dashboard = await dashboardService.setDefault(getContext(req), req.params.id);
      if (!dashboard) {
        res.status(404).json({ error: 'Not Found', message: 'Dashboard not found' });
        return;
      }

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

  /**
   * POST /dashboards/:id/duplicate
   * Duplicar dashboard
   */
  router.post('/:id/duplicate', 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 { code, name } = req.body;
      if (!code || !name) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'code and name are required for duplication',
        });
        return;
      }

      const dashboard = await dashboardService.duplicate(getContext(req), req.params.id, code, name);
      res.status(201).json({ success: true, data: dashboard });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === 'Dashboard not found') {
          res.status(404).json({ error: 'Not Found', message: error.message });
          return;
        }
      }
      next(error);
    }
  });

  /**
   * DELETE /dashboards/:id
   * Eliminar dashboard
   */
  router.delete('/: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;
      }

      // Verificar que no sea un dashboard de sistema
      const dashboard = await dashboardService.findById(getContext(req), req.params.id);
      if (!dashboard) {
        res.status(404).json({ error: 'Not Found', message: 'Dashboard not found' });
        return;
      }

      if (dashboard.isSystem) {
        res.status(403).json({ error: 'Forbidden', message: 'Cannot delete system dashboards' });
        return;
      }

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

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

  // ==================== Widget Endpoints ====================

  /**
   * GET /dashboards/:id/widgets
   * Obtener widgets de un dashboard
   */
  router.get('/:id/widgets', 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 widgets = await dashboardService.getWidgets(getContext(req), req.params.id);
      res.status(200).json({ success: true, data: widgets });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /dashboards/:id/widgets
   * Crear widget en dashboard
   */
  router.post('/:id/widgets', 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 dto: CreateWidgetDto = {
        ...req.body,
        dashboardId: req.params.id,
      };

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

      const widget = await dashboardService.createWidget(getContext(req), dto);
      res.status(201).json({ success: true, data: widget });
    } catch (error) {
      if (error instanceof Error && error.message === 'Dashboard not found') {
        res.status(404).json({ error: 'Not Found', message: error.message });
        return;
      }
      next(error);
    }
  });

  /**
   * PUT /dashboards/:dashboardId/widgets/:widgetId
   * Actualizar widget
   */
  router.put('/:dashboardId/widgets/:widgetId', 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 dto: UpdateWidgetDto = req.body;
      const widget = await dashboardService.updateWidget(getContext(req), req.params.widgetId, dto);

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

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

  /**
   * PUT /dashboards/:id/widgets/positions
   * Actualizar posiciones de widgets
   */
  router.put('/:id/widgets/positions', 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 { positions } = req.body;
      if (!Array.isArray(positions)) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'positions array is required',
        });
        return;
      }

      await dashboardService.updateWidgetPositions(getContext(req), req.params.id, positions);
      res.status(200).json({ success: true, message: 'Widget positions updated' });
    } catch (error) {
      next(error);
    }
  });

  /**
   * DELETE /dashboards/:dashboardId/widgets/:widgetId
   * Eliminar widget
   */
  router.delete('/:dashboardId/widgets/:widgetId', 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 deleted = await dashboardService.deleteWidget(getContext(req), req.params.widgetId);
      if (!deleted) {
        res.status(404).json({ error: 'Not Found', message: 'Widget not found' });
        return;
      }

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

  return router;
}

export default createDashboardController;
