import { Request, Response, NextFunction, Router } from 'express';
import { UserPreferencesService } from '../services';
import {
  UpdateUserPreferenceDto,
  BulkUpdateUserPreferencesDto,
  SetUserPreferencesDto,
  UserPreferencesFiltersDto,
} from '../dto';
import { logger } from '../../../shared/utils/logger.js';

/**
 * UserPreferencesController
 *
 * REST API for managing user-specific preferences.
 *
 * Endpoints:
 * - GET    /api/v1/settings/user             - Get all user preferences
 * - GET    /api/v1/settings/user/grouped     - Get preferences grouped by category
 * - GET    /api/v1/settings/user/:key        - Get preference by key
 * - PATCH  /api/v1/settings/user/:key        - Update preference
 * - PATCH  /api/v1/settings/user             - Bulk update preferences
 * - PUT    /api/v1/settings/user             - Set all preferences
 * - POST   /api/v1/settings/user/:key/reset  - Reset preference to default
 * - POST   /api/v1/settings/user/reset       - Reset all preferences
 * - DELETE /api/v1/settings/user/:key        - Delete preference
 */
export class UserPreferencesController {
  public router: Router;

  constructor(private readonly userPreferencesService: UserPreferencesService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    this.router.get('/grouped', this.getGrouped.bind(this));
    this.router.get('/', this.getAll.bind(this));
    this.router.get('/:key', this.get.bind(this));
    this.router.patch('/:key', this.update.bind(this));
    this.router.patch('/', this.bulkUpdate.bind(this));
    this.router.put('/', this.setAll.bind(this));
    this.router.post('/reset', this.resetAll.bind(this));
    this.router.post('/:key/reset', this.reset.bind(this));
    this.router.delete('/:key', this.delete.bind(this));
  }

  /**
   * Helper to get user ID from request
   */
  private getUserId(req: Request): string | null {
    return (req.headers['x-user-id'] as string) || null;
  }

  /**
   * GET /api/v1/settings/user
   * Get all user preferences
   */
  private async getAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const { category, search } = req.query;

      const filters: UserPreferencesFiltersDto = {
        category: category as string,
        search: search as string,
      };

      const preferences = await this.userPreferencesService.getAll(userId, filters);

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

  /**
   * GET /api/v1/settings/user/grouped
   * Get all preferences grouped by category
   */
  private async getGrouped(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const grouped = await this.userPreferencesService.getGroupedByCategory(userId);

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

  /**
   * GET /api/v1/settings/user/:key
   * Get a single preference by key
   */
  private async get(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const { key } = req.params;
      const value = await this.userPreferencesService.getValue(userId, key);

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

  /**
   * PATCH /api/v1/settings/user/:key
   * Update a single preference
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const { key } = req.params;
      const dto: UpdateUserPreferenceDto = req.body;

      const preference = await this.userPreferencesService.update(userId, key, dto);

      logger.debug('User preference updated via API', { userId, key });

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

  /**
   * PATCH /api/v1/settings/user
   * Bulk update preferences
   */
  private async bulkUpdate(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const dto: BulkUpdateUserPreferencesDto = req.body;
      const preferences = await this.userPreferencesService.bulkUpdate(userId, dto);

      logger.info('User preferences bulk updated via API', {
        userId,
        count: dto.preferences.length,
      });

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

  /**
   * PUT /api/v1/settings/user
   * Set all standard preferences at once
   */
  private async setAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const dto: SetUserPreferencesDto = req.body;
      const preferences = await this.userPreferencesService.setPreferences(userId, dto);

      logger.info('User preferences set via API', { userId });

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

  /**
   * POST /api/v1/settings/user/:key/reset
   * Reset a preference to default
   */
  private async reset(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const { key } = req.params;
      const preference = await this.userPreferencesService.reset(userId, key);

      logger.debug('User preference reset via API', { userId, key });

      res.json({
        success: true,
        data: preference,
        message: `Preference '${key}' reset to default`,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /api/v1/settings/user/reset
   * Reset all preferences to defaults
   */
  private async resetAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const preferences = await this.userPreferencesService.resetAll(userId);

      logger.info('User preferences reset to defaults via API', { userId });

      res.json({
        success: true,
        data: preferences,
        total: preferences.length,
        message: 'All preferences reset to defaults',
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /api/v1/settings/user/:key
   * Delete a preference
   */
  private async delete(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = this.getUserId(req);
      if (!userId) {
        res.status(400).json({
          success: false,
          error: 'User ID is required',
        });
        return;
      }

      const { key } = req.params;
      const deleted = await this.userPreferencesService.delete(userId, key);

      if (!deleted) {
        res.status(404).json({
          success: false,
          error: `Preference '${key}' not found`,
        });
        return;
      }

      logger.debug('User preference deleted via API', { userId, key });

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }
}
