import { NotificationRecipients } from "@edr/types";
import { Injectable, Logger } from "@nestjs/common";

import { BackofficeService } from "../backoffice/backoffice.service";
import { CompanyProfileRepository } from "../companies/company-profile.repository";
import { ExternalProfileRepository } from "../companies/external-profile.repository";

/**
 * Turns a {@link NotificationRecipients} selector into a de-duplicated set of
 * IAM user ids.
 *
 * - `userIds`          → honored as-is.
 * - `companyId`        → all portal users linked to the company (external_profiles).
 * - `companyProfileId` → resolved to its company, then to that company's users.
 * - `organizationId`   → all current employees of the org (backoffice staff).
 *
 * NOTE: permission-scoped staff targeting is intentionally unsupported — freight
 * has no "users-by-permission" lookup. Target explicit userIds or an org instead.
 */
@Injectable()
export class NotificationRecipientsService {
  private readonly logger = new Logger(NotificationRecipientsService.name);

  constructor(
    private readonly externalProfiles: ExternalProfileRepository,
    private readonly companyProfiles: CompanyProfileRepository,
    private readonly backoffice: BackofficeService,
  ) {}

  async resolve(recipients: NotificationRecipients): Promise<string[]> {
    const ids = new Set<string>();

    for (const id of recipients.userIds ?? []) {
      if (id) ids.add(id);
    }

    let companyId = recipients.companyId;
    if (!companyId && recipients.companyProfileId) {
      const profile = await this.companyProfiles.findById(
        recipients.companyProfileId,
      );
      companyId = profile?.companyId ?? undefined;
    }
    if (companyId) {
      const profiles = await this.externalProfiles.findByCompanyId(companyId);
      for (const p of profiles) {
        if (p.userId) ids.add(p.userId);
      }
    }

    if (recipients.organizationId) {
      try {
        const { items } = await this.backoffice.getOrganizationEmployees(
          recipients.organizationId,
          {},
        );
        for (const employee of items as Array<{
          user?: { id?: string };
          userId?: string;
        }>) {
          const uid = employee?.user?.id ?? employee?.userId;
          if (uid) ids.add(uid);
        }
      } catch (err) {
        this.logger.warn(
          `Failed to resolve org recipients for ${recipients.organizationId}: ${
            (err as Error).message
          }`,
        );
      }
    }

    if (recipients.allBackoffice) {
      try {
        for (const uid of await this.backoffice.getAllCurrentEmployeeUserIds()) {
          ids.add(uid);
        }
      } catch (err) {
        this.logger.warn(
          `Failed to resolve allBackoffice recipients: ${(err as Error).message}`,
        );
      }
    }

    return [...ids];
  }
}
