import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseTenantRepository, TenantContextService } from '@iconsulting/shared';
import { IUserContactRepository } from '../../../domain/repositories/user-contact.repository.interface';
import {
  UserContactEntity,
  ContactType,
  NotificationType,
} from '../../../domain/entities/user-contact.entity';
import { UserContactORM } from '../../../infrastructure/database/postgres/entities/user-contact.orm';

@Injectable()
export class UserContactPostgresRepository
  extends BaseTenantRepository<UserContactEntity, UserContactORM>
  implements IUserContactRepository
{
  constructor(
    @InjectRepository(UserContactORM) repo: Repository<UserContactORM>,
    tenantContext: TenantContextService,
  ) {
    super(repo, tenantContext);
  }

  async save(contact: UserContactEntity): Promise<void> {
    const orm = this.toORM(contact);
    orm.tenantId = this.getTenantId();
    await this.repo.save(orm);
  }

  async findById(id: string): Promise<UserContactEntity | null> {
    const orm = await this.findOneWithTenant({ id } as any);
    return orm ? this.toEntity(orm) : null;
  }

  async findByUserIdAndType(userId: string, type: ContactType): Promise<UserContactEntity | null> {
    const orm = await this.findOneWithTenant({ userId, type } as any);
    return orm ? this.toEntity(orm) : null;
  }

  async findByUserId(userId: string): Promise<UserContactEntity[]> {
    const orms = await this.repo.find({
      where: { userId, tenantId: this.getTenantId() } as any,
      order: { createdAt: 'ASC' },
    });
    return orms.map(orm => this.toEntity(orm));
  }

  async findVerifiedByUserId(userId: string): Promise<UserContactEntity[]> {
    const orms = await this.repo.find({
      where: { userId, tenantId: this.getTenantId(), isVerified: true } as any,
      order: { createdAt: 'ASC' },
    });
    return orms.map(orm => this.toEntity(orm));
  }

  async update(contact: UserContactEntity): Promise<void> {
    const orm = this.toORM(contact);
    orm.tenantId = this.getTenantId();
    await this.repo.save(orm);
  }

  async delete(id: string): Promise<void> {
    await this.repo.delete({ id, tenantId: this.getTenantId() } as any);
  }

  async findByNotificationType(
    notificationType: NotificationType,
    options?: { limit?: number; offset?: number },
  ): Promise<UserContactEntity[]> {
    const query = this.createTenantQueryBuilder('contact')
      .andWhere('contact.notification_enabled = true')
      .andWhere('contact.is_verified = true')
      .andWhere(':type = ANY(contact.enabled_notification_types)', { type: notificationType })
      .orderBy('contact.created_at', 'ASC');

    if (options?.limit) {
      query.take(options.limit);
    }
    if (options?.offset) {
      query.skip(options.offset);
    }

    const orms = await query.getMany();
    return orms.map(orm => this.toEntity(orm));
  }

  async countVerifiedByType(): Promise<Record<ContactType, number>> {
    const tenantId = this.getTenantId();

    const result = await this.repo
      .createQueryBuilder('contact')
      .select('contact.type', 'type')
      .addSelect('COUNT(*)', 'count')
      .where('contact.tenant_id = :tenantId', { tenantId })
      .andWhere('contact.is_verified = true')
      .groupBy('contact.type')
      .getRawMany();

    const counts: Record<ContactType, number> = {
      [ContactType.EMAIL]: 0,
      [ContactType.WECHAT]: 0,
      [ContactType.WHATSAPP]: 0,
      [ContactType.TELEGRAM]: 0,
      [ContactType.LINE]: 0,
    };

    for (const row of result) {
      counts[row.type as ContactType] = parseInt(row.count, 10);
    }

    return counts;
  }

  private toORM(entity: UserContactEntity): UserContactORM {
    const orm = new UserContactORM();
    orm.id = entity.id;
    orm.tenantId = this.getTenantId();
    orm.userId = entity.userId;
    orm.type = entity.type;
    orm.value = entity.value;
    orm.displayName = entity.displayName;
    orm.isVerified = entity.isVerified;
    orm.verifiedAt = entity.verifiedAt;
    orm.notificationEnabled = entity.notificationEnabled;
    orm.enabledNotificationTypes = entity.enabledNotificationTypes;
    orm.verificationCode = entity.verificationCode;
    orm.verificationExpiresAt = entity.verificationExpiresAt;
    orm.createdAt = entity.createdAt;
    orm.updatedAt = entity.updatedAt;
    return orm;
  }

  private toEntity(orm: UserContactORM): UserContactEntity {
    return UserContactEntity.fromPersistence({
      id: orm.id,
      userId: orm.userId,
      type: orm.type,
      value: orm.value,
      displayName: orm.displayName,
      isVerified: orm.isVerified,
      verifiedAt: orm.verifiedAt,
      notificationEnabled: orm.notificationEnabled,
      enabledNotificationTypes: orm.enabledNotificationTypes,
      verificationCode: orm.verificationCode,
      verificationExpiresAt: orm.verificationExpiresAt,
      createdAt: orm.createdAt,
      updatedAt: orm.updatedAt,
    });
  }
}
