import { Request, Response, NextFunction, Router } from 'express';
import { SystemSettingsService } from '../services';
import { UpdateSystemSettingDto, CreateSystemSettingDto, SystemSettingsFiltersDto } from '../dto';
import { logger } from '../../../shared/utils/logger.js';

/**
 * SystemSettingsController
 *
 * REST API for managing global system settings.
 *
 * Endpoints:
 * - GET    /api/v1/settings/system           - List all settings
 * - GET    /api/v1/settings/system/public    - List public settings (no auth)
 * - GET    /api/v1/settings/system/categories - List categories
 * - GET    /api/v1/settings/system/:key      - Get setting by key
 * - PUT    /api/v1/settings/system/:key      - Update setting
 * - POST   /api/v1/settings/system/:key/reset - Reset to default
 * - POST   /api/v1/settings/system           - Create new setting (admin)
 */
export class SystemSettingsController {
  public router: Router;

  constructor(private readonly systemSettingsService: SystemSettingsService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Public routes (no authentication required)
    this.router.get('/public', this.getPublicSettings.bind(this));

    // Protected routes
    this.router.get('/categories', this.getCategories.bind(this));
    this.router.get('/', this.getAll.bind(this));
    this.router.get('/:key', this.getByKey.bind(this));
    this.router.put('/:key', this.update.bind(this));
    this.router.post('/:key/reset', this.reset.bind(this));
    this.router.post('/', this.create.bind(this));
  }

  /**
   * GET /api/v1/settings/system
   * Get all system settings with optional filtering
   */
  private async getAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { category, isPublic, isEditable, search } = req.query;

      const filters: SystemSettingsFiltersDto = {
        category: category as string,
        isPublic: isPublic !== undefined ? isPublic === 'true' : undefined,
        isEditable: isEditable !== undefined ? isEditable === 'true' : undefined,
        search: search as string,
      };

      const settings = await this.systemSettingsService.getAll(filters);

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

  /**
   * GET /api/v1/settings/system/public
   * Get all public system settings (no authentication required)
   */
  private async getPublicSettings(_req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const settings = await this.systemSettingsService.getPublicSettings();

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

  /**
   * GET /api/v1/settings/system/categories
   * Get all unique setting categories
   */
  private async getCategories(_req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const categories = await this.systemSettingsService.getCategories();

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

  /**
   * GET /api/v1/settings/system/:key
   * Get a single setting by key
   */
  private async getByKey(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { key } = req.params;
      const setting = await this.systemSettingsService.getByKey(key);

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

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

  /**
   * PUT /api/v1/settings/system/:key
   * Update a system setting
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { key } = req.params;
      const userId = req.headers['x-user-id'] as string;
      const dto: UpdateSystemSettingDto = req.body;

      const setting = await this.systemSettingsService.update(key, dto, userId);

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

      logger.info('System setting updated via API', { key, userId });

      res.json({
        success: true,
        data: setting,
      });
    } catch (error) {
      if ((error as Error).message.includes('not editable')) {
        res.status(403).json({
          success: false,
          error: (error as Error).message,
        });
        return;
      }
      next(error);
    }
  }

  /**
   * POST /api/v1/settings/system/:key/reset
   * Reset a setting to its default value
   */
  private async reset(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { key } = req.params;
      const userId = req.headers['x-user-id'] as string;

      const setting = await this.systemSettingsService.reset(key, userId);

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

      logger.info('System setting reset via API', { key, userId });

      res.json({
        success: true,
        data: setting,
      });
    } catch (error) {
      if ((error as Error).message.includes('no default value')) {
        res.status(400).json({
          success: false,
          error: (error as Error).message,
        });
        return;
      }
      next(error);
    }
  }

  /**
   * POST /api/v1/settings/system
   * Create a new system setting (admin only)
   */
  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = req.headers['x-user-id'] as string;
      const dto: CreateSystemSettingDto = req.body;

      const setting = await this.systemSettingsService.create(dto, userId);

      logger.info('System setting created via API', { key: dto.key, userId });

      res.status(201).json({
        success: true,
        data: setting,
      });
    } catch (error) {
      if ((error as Error).message.includes('already exists')) {
        res.status(409).json({
          success: false,
          error: (error as Error).message,
        });
        return;
      }
      next(error);
    }
  }
}
