import { Request, Response, NextFunction, Router } from 'express';
import { DashboardsService } from '../services/dashboards.service';
import {
  CreateDashboardDto,
  UpdateDashboardDto,
  CreateDashboardWidgetDto,
  UpdateDashboardWidgetDto,
  CreateWidgetQueryDto,
  UpdateWidgetQueryDto,
  UpdateWidgetPositionsDto,
  DashboardFiltersDto,
} from '../dto';
import { logger } from '../../../shared/utils/logger.js';

/**
 * DashboardsController
 *
 * REST API for managing dashboards, widgets, and widget queries.
 */
export class DashboardsController {
  public router: Router;

  constructor(private readonly dashboardsService: DashboardsService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Dashboards
    this.router.get('/', this.getDashboards.bind(this));
    this.router.get('/default', this.getDefaultDashboard.bind(this));
    this.router.get('/:id', this.getDashboardById.bind(this));
    this.router.get('/:id/data', this.getDashboardData.bind(this));
    this.router.post('/', this.createDashboard.bind(this));
    this.router.put('/:id', this.updateDashboard.bind(this));
    this.router.delete('/:id', this.deleteDashboard.bind(this));

    // Widgets
    this.router.get('/:dashboardId/widgets', this.getWidgets.bind(this));
    this.router.post('/:dashboardId/widgets', this.createWidget.bind(this));
    this.router.put('/widgets/:id', this.updateWidget.bind(this));
    this.router.patch('/widgets/positions', this.updateWidgetPositions.bind(this));
    this.router.delete('/widgets/:id', this.deleteWidget.bind(this));

    // Widget Queries
    this.router.post('/widgets/:widgetId/queries', this.createWidgetQuery.bind(this));
    this.router.put('/queries/:id', this.updateWidgetQuery.bind(this));
    this.router.delete('/queries/:id', this.deleteWidgetQuery.bind(this));
    this.router.post('/queries/:id/execute', this.executeWidgetQuery.bind(this));
  }

  // ==================== DASHBOARDS ====================

  private async getDashboards(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 { ownerId, isDefault, isPublic, isActive, search, page, limit } = req.query;

      const filters: DashboardFiltersDto = {
        ownerId: ownerId as string,
        isDefault: isDefault !== undefined ? isDefault === 'true' : undefined,
        isPublic: isPublic !== undefined ? isPublic === 'true' : undefined,
        isActive: isActive !== undefined ? isActive === 'true' : undefined,
        search: search as string,
        page: page ? parseInt(page as string, 10) : undefined,
        limit: limit ? parseInt(limit as string, 10) : undefined,
      };

      const { data, total } = await this.dashboardsService.findAllDashboards(tenantId, userId, filters);

      res.json({
        success: true,
        data,
        pagination: {
          page: filters.page || 1,
          limit: filters.limit || 20,
          total,
          totalPages: Math.ceil(total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  private async getDefaultDashboard(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 dashboard = await this.dashboardsService.getDefaultDashboard(tenantId, userId);

      if (!dashboard) {
        res.status(404).json({
          success: false,
          error: 'No default dashboard found',
        });
        return;
      }

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

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

      const dashboard = await this.dashboardsService.findDashboardById(id, tenantId, userId);

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

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

      const data = await this.dashboardsService.getDashboardData(id, tenantId, userId);

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

  private async createDashboard(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 dto: CreateDashboardDto = req.body;

      const dashboard = await this.dashboardsService.createDashboard(dto, tenantId, userId);

      logger.info('Dashboard created via API', { id: dashboard.id, userId });

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

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

      const dashboard = await this.dashboardsService.updateDashboard(id, dto, tenantId, userId);

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

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

      await this.dashboardsService.deleteDashboard(id, tenantId, userId);

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

  // ==================== WIDGETS ====================

  private async getWidgets(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { dashboardId } = req.params;

      const widgets = await this.dashboardsService.findWidgetsByDashboard(dashboardId);

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

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

      const widget = await this.dashboardsService.createWidget(dto, tenantId, userId);

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

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

      const widget = await this.dashboardsService.updateWidget(id, dto, tenantId, userId);

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

  private async updateWidgetPositions(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 dto: UpdateWidgetPositionsDto = req.body;

      await this.dashboardsService.updateWidgetPositions(dto, tenantId, userId);

      res.json({
        success: true,
        message: 'Widget positions updated',
      });
    } catch (error) {
      next(error);
    }
  }

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

      await this.dashboardsService.deleteWidget(id, tenantId, userId);

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

  // ==================== WIDGET QUERIES ====================

  private async createWidgetQuery(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { widgetId } = req.params;
      const dto: CreateWidgetQueryDto = { ...req.body, widgetId };

      const query = await this.dashboardsService.createWidgetQuery(dto);

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

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

      const query = await this.dashboardsService.updateWidgetQuery(id, dto);

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

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

      await this.dashboardsService.deleteWidgetQuery(id);

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

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

      const result = await this.dashboardsService.executeWidgetQuery(id, tenantId, parameters);

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