import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TenantContextService } from '@iconsulting/shared';
import { IAdminRepository } from '../../../domain/repositories/admin.repository.interface';
import { AdminEntity } from '../../../domain/entities/admin.entity';
import { AdminRole } from '../../../domain/value-objects/admin-role.enum';
import { AdminORM } from '../../../infrastructure/database/postgres/entities/admin.orm';

@Injectable()
export class AdminPostgresRepository implements IAdminRepository {
  constructor(
    @InjectRepository(AdminORM)
    private adminRepo: Repository<AdminORM>,
    private readonly tenantContext: TenantContextService,
  ) {}

  private getTenantId(): string {
    const id = this.tenantContext.getCurrentTenantId();
    if (!id) throw new Error('Tenant context not set');
    return id;
  }

  async save(admin: AdminEntity): Promise<void> {
    const orm = this.toORM(admin);
    orm.tenantId = this.getTenantId();
    await this.adminRepo.save(orm);
  }

  async findById(id: string): Promise<AdminEntity | null> {
    const orm = await this.adminRepo.findOne({
      where: { id, tenantId: this.getTenantId() },
    });
    return orm ? this.toEntity(orm) : null;
  }

  async findByUsername(username: string): Promise<AdminEntity | null> {
    // Try to find by username with tenant context first
    try {
      const tenantId = this.getTenantId();
      const orm = await this.adminRepo.findOne({
        where: { username, tenantId },
      });
      if (orm) return this.toEntity(orm);
    } catch (error) {
      // If no tenant context, continue to check for super admin
    }

    // Check for super admin without tenant constraint
    const orm = await this.adminRepo.findOne({
      where: { username, isSuperAdmin: true },
    });
    return orm ? this.toEntity(orm) : null;
  }

  async findAll(options?: {
    role?: AdminRole;
    isActive?: boolean;
    limit?: number;
    offset?: number;
  }): Promise<AdminEntity[]> {
    const query = this.adminRepo.createQueryBuilder('admin')
      .where('admin.tenant_id = :tenantId', { tenantId: this.getTenantId() });

    if (options?.role) {
      query.andWhere('admin.role = :role', { role: options.role });
    }

    if (options?.isActive !== undefined) {
      query.andWhere('admin.isActive = :active', { active: options.isActive });
    }

    query.orderBy('admin.createdAt', 'DESC');

    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 count(options?: {
    role?: AdminRole;
    isActive?: boolean;
  }): Promise<number> {
    const query = this.adminRepo.createQueryBuilder('admin')
      .where('admin.tenant_id = :tenantId', { tenantId: this.getTenantId() });

    if (options?.role) {
      query.andWhere('admin.role = :role', { role: options.role });
    }

    if (options?.isActive !== undefined) {
      query.andWhere('admin.isActive = :active', { active: options.isActive });
    }

    return query.getCount();
  }

  async update(admin: AdminEntity): Promise<void> {
    const orm = this.toORM(admin);
    orm.tenantId = this.getTenantId();
    await this.adminRepo.save(orm);
  }

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

  private toORM(entity: AdminEntity): AdminORM {
    const orm = new AdminORM();
    orm.id = entity.id;
    orm.tenantId = this.getTenantId();
    orm.username = entity.username;
    orm.passwordHash = entity.passwordHash;
    orm.name = entity.name;
    orm.email = entity.email;
    orm.phone = entity.phone;
    orm.role = entity.role;
    orm.permissions = entity.permissions;
    orm.avatar = entity.avatar;
    orm.lastLoginAt = entity.lastLoginAt;
    orm.lastLoginIp = entity.lastLoginIp;
    orm.isActive = entity.isActive;
    orm.createdAt = entity.createdAt;
    orm.updatedAt = entity.updatedAt;
    return orm;
  }

  private toEntity(orm: AdminORM): AdminEntity {
    return AdminEntity.fromPersistence({
      id: orm.id,
      username: orm.username,
      passwordHash: orm.passwordHash,
      name: orm.name,
      email: orm.email,
      phone: orm.phone,
      role: orm.role as AdminRole,
      permissions: orm.permissions,
      avatar: orm.avatar,
      lastLoginAt: orm.lastLoginAt,
      lastLoginIp: orm.lastLoginIp,
      isActive: orm.isActive,
      createdAt: orm.createdAt,
      updatedAt: orm.updatedAt,
    });
  }
}
