import { Request, Response, NextFunction, Router } from 'express';
import { TenantSettingsService } from '../services';
import {
  UpdateTenantSettingDto,
  CreateTenantSettingDto,
  BulkUpdateTenantSettingsDto,
  TenantSettingsFiltersDto,
} from '../dto';
import { logger } from '../../../shared/utils/logger.js';

/**
 * TenantSettingsController
 *
 * REST API for managing tenant-specific settings.
 *
 * Endpoints:
 * - GET    /api/v1/settings/tenant           - List all tenant settings
 * - GET    /api/v1/settings/tenant/effective - Get all effective settings (with inheritance)
 * - GET    /api/v1/settings/tenant/:key      - Get setting by key
 * - GET    /api/v1/settings/tenant/:key/effective - Get effective value
 * - PUT    /api/v1/settings/tenant/:key      - Update setting
 * - POST   /api/v1/settings/tenant           - Create new setting
 * - POST   /api/v1/settings/tenant/bulk      - Bulk update settings
 * - POST   /api/v1/settings/tenant/:key/reset - Reset to inherited value
 * - DELETE /api/v1/settings/tenant/:key      - Delete setting override
 */
export class TenantSettingsController {
  public router: Router;

  constructor(private readonly tenantSettingsService: TenantSettingsService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    this.router.get('/effective', this.getAllEffective.bind(this));
    this.router.get('/', this.getAll.bind(this));
    this.router.get('/:key/effective', this.getEffective.bind(this));
    this.router.get('/:key', this.get.bind(this));
    this.router.put('/:key', this.update.bind(this));
    this.router.post('/bulk', this.bulkUpdate.bind(this));
    this.router.post('/:key/reset', this.reset.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.delete('/:key', this.delete.bind(this));
  }

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

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

  /**
   * GET /api/v1/settings/tenant
   * Get all tenant settings with optional filtering
   */
  private async getAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const { inheritedFrom, isOverridden, search } = req.query;

      const filters: TenantSettingsFiltersDto = {
        inheritedFrom: inheritedFrom as 'system' | 'plan' | 'custom',
        isOverridden: isOverridden !== undefined ? isOverridden === 'true' : undefined,
        search: search as string,
      };

      const settings = await this.tenantSettingsService.getAll(tenantId, filters);

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

  /**
   * GET /api/v1/settings/tenant/effective
   * Get all effective settings with inheritance resolution
   */
  private async getAllEffective(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const planId = this.getPlanId(req);
      const settings = await this.tenantSettingsService.getAllEffective(tenantId, planId);

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

  /**
   * GET /api/v1/settings/tenant/:key
   * Get a tenant setting by key (direct, without inheritance)
   */
  private async get(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const { key } = req.params;
      const setting = await this.tenantSettingsService.get(tenantId, key);

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

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

  /**
   * GET /api/v1/settings/tenant/:key/effective
   * Get the effective value for a setting with inheritance
   */
  private async getEffective(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const { key } = req.params;
      const planId = this.getPlanId(req);

      const effective = await this.tenantSettingsService.getEffective(tenantId, key, planId);

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

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

  /**
   * PUT /api/v1/settings/tenant/:key
   * Update a tenant setting
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

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

      const setting = await this.tenantSettingsService.update(tenantId, key, dto);

      logger.info('Tenant setting updated via API', { tenantId, key });

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

  /**
   * POST /api/v1/settings/tenant
   * Create a new tenant setting
   */
  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const dto: CreateTenantSettingDto = req.body;
      const setting = await this.tenantSettingsService.create(tenantId, dto);

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

      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);
    }
  }

  /**
   * POST /api/v1/settings/tenant/bulk
   * Bulk update tenant settings
   */
  private async bulkUpdate(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const dto: BulkUpdateTenantSettingsDto = req.body;
      const settings = await this.tenantSettingsService.bulkUpdate(tenantId, dto.settings);

      logger.info('Tenant settings bulk updated via API', {
        tenantId,
        count: dto.settings.length,
      });

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

  /**
   * POST /api/v1/settings/tenant/:key/reset
   * Reset a tenant setting to inherited value
   */
  private async reset(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = this.getTenantId(req);
      if (!tenantId) {
        res.status(400).json({
          success: false,
          error: 'Tenant ID is required',
        });
        return;
      }

      const { key } = req.params;
      const deleted = await this.tenantSettingsService.reset(tenantId, key);

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

      logger.info('Tenant setting reset via API', { tenantId, key });

      res.json({
        success: true,
        message: `Setting '${key}' reset to inherited value`,
      });
    } catch (error) {
      next(error);
    }
  }

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

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

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

      logger.info('Tenant setting deleted via API', { tenantId, key });

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