/**
 * WebhookEventTypeService - Webhook Event Type Management
 *
 * CRUD operations for webhook event type definitions.
 * Manages the catalog of available events that can trigger webhooks.
 *
 * @module Webhooks
 */

import { Repository } from 'typeorm';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';
import { WebhookEventType, EventCategory } from '../entities/event-type.entity';

// DTOs
export interface CreateEventTypeDto {
  code: string;
  name: string;
  description?: string;
  category?: EventCategory;
  payloadSchema?: Record<string, any>;
  isInternal?: boolean;
  metadata?: Record<string, any>;
}

export interface UpdateEventTypeDto extends Partial<Omit<CreateEventTypeDto, 'code'>> {
  isActive?: boolean;
}

export interface EventTypeFilters {
  category?: EventCategory;
  isActive?: boolean;
  isInternal?: boolean;
  search?: string;
  page?: number;
  limit?: number;
}

export class WebhookEventTypeService {
  constructor(private readonly repository: Repository<WebhookEventType>) {}

  /**
   * Create a new event type
   */
  async create(_ctx: ServiceContext, dto: CreateEventTypeDto): Promise<WebhookEventType> {
    // Check for duplicate code (codes are globally unique)
    const existing = await this.repository.findOne({
      where: { code: dto.code },
    });

    if (existing) {
      throw new Error('Event type with this code already exists');
    }

    const eventType = this.repository.create({
      code: dto.code,
      name: dto.name,
      description: dto.description,
      category: dto.category,
      payloadSchema: dto.payloadSchema || {},
      isActive: true,
      isInternal: dto.isInternal ?? false,
      metadata: dto.metadata || {},
    });

    return this.repository.save(eventType);
  }

  /**
   * Find all event types with filters and pagination
   */
  async findAll(
    _ctx: ServiceContext,
    filters: EventTypeFilters = {}
  ): Promise<PaginatedResult<WebhookEventType>> {
    const page = filters.page || 1;
    const limit = filters.limit || 50;

    const qb = this.repository.createQueryBuilder('et');

    if (filters.category) {
      qb.andWhere('et.category = :category', { category: filters.category });
    }

    if (filters.isActive !== undefined) {
      qb.andWhere('et.is_active = :isActive', { isActive: filters.isActive });
    }

    if (filters.isInternal !== undefined) {
      qb.andWhere('et.is_internal = :isInternal', { isInternal: filters.isInternal });
    }

    if (filters.search) {
      qb.andWhere(
        '(et.code ILIKE :search OR et.name ILIKE :search OR et.description ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    const skip = (page - 1) * limit;
    qb.orderBy('et.category', 'ASC').addOrderBy('et.code', 'ASC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Find event type by ID
   */
  async findById(_ctx: ServiceContext, id: string): Promise<WebhookEventType | null> {
    return this.repository.findOne({
      where: { id },
    });
  }

  /**
   * Find event type by code
   */
  async findByCode(_ctx: ServiceContext, code: string): Promise<WebhookEventType | null> {
    return this.repository.findOne({
      where: { code },
    });
  }

  /**
   * Find event types by category
   */
  async findByCategory(
    _ctx: ServiceContext,
    category: EventCategory
  ): Promise<WebhookEventType[]> {
    return this.repository.find({
      where: { category, isActive: true },
      order: { code: 'ASC' },
    });
  }

  /**
   * Get all active event types (for subscriptions)
   */
  async getActiveEventTypes(_ctx: ServiceContext): Promise<WebhookEventType[]> {
    return this.repository.find({
      where: { isActive: true, isInternal: false },
      order: { category: 'ASC', code: 'ASC' },
    });
  }

  /**
   * Update event type
   */
  async update(
    ctx: ServiceContext,
    id: string,
    dto: UpdateEventTypeDto
  ): Promise<WebhookEventType | null> {
    const eventType = await this.findById(ctx, id);
    if (!eventType) return null;

    if (dto.name !== undefined) eventType.name = dto.name;
    if (dto.description !== undefined) eventType.description = dto.description;
    if (dto.category !== undefined) eventType.category = dto.category;
    if (dto.payloadSchema !== undefined) eventType.payloadSchema = dto.payloadSchema;
    if (dto.isActive !== undefined) eventType.isActive = dto.isActive;
    if (dto.isInternal !== undefined) eventType.isInternal = dto.isInternal;
    if (dto.metadata !== undefined) eventType.metadata = dto.metadata;

    return this.repository.save(eventType);
  }

  /**
   * Delete event type (hard delete)
   */
  async delete(_ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.repository.delete({ id });
    return (result.affected || 0) > 0;
  }

  /**
   * Seed default event types (for initialization)
   */
  async seedDefaults(): Promise<void> {
    const defaults: CreateEventTypeDto[] = [
      // Sales events
      { code: 'sales.order.created', name: 'Sales Order Created', category: 'sales' },
      { code: 'sales.order.updated', name: 'Sales Order Updated', category: 'sales' },
      { code: 'sales.order.completed', name: 'Sales Order Completed', category: 'sales' },
      { code: 'sales.order.cancelled', name: 'Sales Order Cancelled', category: 'sales' },

      // Inventory events
      { code: 'inventory.stock.low', name: 'Low Stock Alert', category: 'inventory' },
      { code: 'inventory.stock.updated', name: 'Stock Updated', category: 'inventory' },
      { code: 'inventory.transfer.created', name: 'Transfer Created', category: 'inventory' },

      // Customer events
      { code: 'customers.created', name: 'Customer Created', category: 'customers' },
      { code: 'customers.updated', name: 'Customer Updated', category: 'customers' },

      // Auth events
      { code: 'auth.user.created', name: 'User Created', category: 'auth' },
      { code: 'auth.user.updated', name: 'User Updated', category: 'auth' },
      { code: 'auth.login.failed', name: 'Login Failed', category: 'auth', isInternal: true },

      // Billing events
      { code: 'billing.invoice.created', name: 'Invoice Created', category: 'billing' },
      { code: 'billing.invoice.paid', name: 'Invoice Paid', category: 'billing' },
      { code: 'billing.payment.received', name: 'Payment Received', category: 'billing' },

      // System events
      { code: 'system.backup.completed', name: 'Backup Completed', category: 'system' },
      { code: 'system.maintenance.scheduled', name: 'Maintenance Scheduled', category: 'system' },
    ];

    for (const dto of defaults) {
      const existing = await this.repository.findOne({ where: { code: dto.code } });
      if (!existing) {
        await this.repository.save(this.repository.create({
          ...dto,
          isActive: true,
          payloadSchema: {},
          metadata: {},
        }));
      }
    }
  }

  /**
   * Get event types grouped by category
   */
  async getGroupedByCategory(
    _ctx: ServiceContext
  ): Promise<Record<string, WebhookEventType[]>> {
    const eventTypes = await this.repository.find({
      where: { isActive: true, isInternal: false },
      order: { code: 'ASC' },
    });

    const grouped: Record<string, WebhookEventType[]> = {};
    for (const et of eventTypes) {
      const category = et.category || 'other';
      if (!grouped[category]) {
        grouped[category] = [];
      }
      grouped[category].push(et);
    }

    return grouped;
  }
}
