/**
 * TypeORM Webhook Repository Implementation
 */

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WebhookRepository, WebhookFilters } from '@domain/ports/out/webhook.repository';
import { Webhook, WebhookEvent, WebhookStatus } from '@domain/entities/webhook.entity';
import { WebhookOrmEntity } from '../entities/webhook.orm-entity';

@Injectable()
export class TypeOrmWebhookRepository implements WebhookRepository {
  constructor(
    @InjectRepository(WebhookOrmEntity)
    private readonly ormRepository: Repository<WebhookOrmEntity>
  ) {}

  async save(webhook: Webhook): Promise<void> {
    const ormEntity = this.toOrm(webhook);
    await this.ormRepository.save(ormEntity);
  }

  async findById(id: string): Promise<Webhook | null> {
    const ormEntity = await this.ormRepository.findOne({ where: { id } });
    return ormEntity ? this.toDomain(ormEntity) : null;
  }

  async findByOrganization(organizationId: string): Promise<Webhook[]> {
    const ormEntities = await this.ormRepository.find({
      where: { organization_id: organizationId },
      order: { created_at: 'DESC' },
    });

    return ormEntities.map(e => this.toDomain(e));
  }

  async findActiveByEvent(event: WebhookEvent, organizationId: string): Promise<Webhook[]> {
    const ormEntities = await this.ormRepository
      .createQueryBuilder('webhook')
      .where('webhook.organization_id = :organizationId', { organizationId })
      .andWhere('webhook.status = :status', { status: WebhookStatus.ACTIVE })
      .andWhere(':event = ANY(webhook.events)', { event })
      .getMany();

    return ormEntities.map(e => this.toDomain(e));
  }

  async findByFilters(filters: WebhookFilters): Promise<Webhook[]> {
    const query = this.ormRepository.createQueryBuilder('webhook');

    if (filters.organizationId) {
      query.andWhere('webhook.organization_id = :organizationId', {
        organizationId: filters.organizationId,
      });
    }

    if (filters.status && filters.status.length > 0) {
      query.andWhere('webhook.status IN (:...statuses)', { statuses: filters.status });
    }

    if (filters.event) {
      query.andWhere(':event = ANY(webhook.events)', { event: filters.event });
    }

    query.orderBy('webhook.created_at', 'DESC');

    const ormEntities = await query.getMany();
    return ormEntities.map(e => this.toDomain(e));
  }

  async delete(id: string): Promise<void> {
    await this.ormRepository.delete(id);
  }

  async countByOrganization(organizationId: string): Promise<number> {
    return this.ormRepository.count({
      where: { organization_id: organizationId },
    });
  }

  private toDomain(ormEntity: WebhookOrmEntity): Webhook {
    return Webhook.fromPersistence({
      id: ormEntity.id,
      organizationId: ormEntity.organization_id,
      url: ormEntity.url,
      events: ormEntity.events as WebhookEvent[],
      secret: ormEntity.secret,
      status: ormEntity.status as WebhookStatus,
      description: ormEntity.description,
      headers: ormEntity.headers,
      retryCount: ormEntity.retry_count,
      lastTriggeredAt: ormEntity.last_triggered_at,
      failureCount: ormEntity.failure_count,
      createdAt: ormEntity.created_at,
      updatedAt: ormEntity.updated_at,
    });
  }

  private toOrm(webhook: Webhook): WebhookOrmEntity {
    const ormEntity = new WebhookOrmEntity();
    ormEntity.id = webhook.id;
    ormEntity.organization_id = webhook.organizationId;
    ormEntity.url = webhook.url;
    ormEntity.events = webhook.events;
    ormEntity.secret = webhook.secret;
    ormEntity.status = webhook.status;
    ormEntity.description = webhook.description;
    ormEntity.headers = webhook.headers;
    ormEntity.retry_count = webhook.retryCount;
    ormEntity.last_triggered_at = webhook.lastTriggeredAt;
    ormEntity.failure_count = webhook.failureCount;
    ormEntity.created_at = webhook.createdAt;
    ormEntity.updated_at = webhook.updatedAt;
    return ormEntity;
  }
}
