import { Injectable } from '@nestjs/common';
import type { Prisma } from '@prisma/client';
import { SEARCH_MAX_PER_KIND, SEARCH_MIN_CHARS, type SearchResponse } from '@siop/shared';
import type { AuthenticatedUser } from '../auth/current-user.decorator';
import { PermissionsService } from '../permissions/permissions.service';
import { PrismaService } from '../prisma/prisma.service';

/** Recherche globale de la topbar (⌘K). Chaque famille n'est interrogée que
 *  si le rôle a la permission `view` du domaine ; les OT respectent en plus
 *  l'invariant « voir autre » (même règle que la liste des OT). */
@Injectable()
export class SearchService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly permissions: PermissionsService,
  ) {}

  async search(user: AuthenticatedUser, q: string): Promise<SearchResponse> {
    const terme = q.trim();
    const vide: SearchResponse = { workOrders: [], assets: [], sites: [] };
    if (terme.length < SEARCH_MIN_CHARS) return vide;

    const [voitOT, voitParc, voitSites] = await Promise.all([
      this.permissions.can(user.roleId, 'WORK_ORDERS', 'view'),
      this.permissions.can(user.roleId, 'ASSETS', 'view'),
      this.permissions.can(user.roleId, 'LOCATIONS', 'view'),
    ]);
    const contient = (champ: string): Prisma.StringFilter => ({
      contains: champ,
      mode: 'insensitive',
    });

    const [workOrders, assets, sites] = await Promise.all([
      voitOT
        ? this.prisma.workOrder.findMany({
            where: {
              AND: [
                { OR: [{ reference: contient(terme) }, { title: contient(terme) }] },
                await this.scopeOT(user),
              ],
            },
            select: { id: true, reference: true, title: true, status: true },
            orderBy: { createdAt: 'desc' },
            take: SEARCH_MAX_PER_KIND,
          })
        : [],
      voitParc
        ? this.prisma.asset.findMany({
            where: {
              OR: [
                { reference: contient(terme) },
                { brand: contient(terme) },
                { model: contient(terme) },
              ],
            },
            select: {
              id: true,
              reference: true,
              brand: true,
              model: true,
              location: { select: { name: true, parent: { select: { name: true } } } },
            },
            orderBy: { reference: 'asc' },
            take: SEARCH_MAX_PER_KIND,
          })
        : [],
      voitSites
        ? this.prisma.location.findMany({
            where: {
              parentId: null,
              OR: [{ name: contient(terme) }, { city: contient(terme) }],
            },
            select: { id: true, name: true, city: true },
            orderBy: { name: 'asc' },
            take: SEARCH_MAX_PER_KIND,
          })
        : [],
    ]);

    return {
      workOrders,
      assets: assets.map((a) => ({
        id: a.id,
        reference: a.reference,
        brand: a.brand,
        model: a.model,
        siteName: a.location.parent?.name ?? a.location.name,
      })),
      sites,
    };
  }

  /** Même invariant que WorkOrdersService.scope — sans le droit « voir
   *  autre », on ne trouve que SES OT. */
  private async scopeOT(user: AuthenticatedUser): Promise<Prisma.WorkOrderWhereInput> {
    const viewOther = await this.permissions.can(user.roleId, 'WORK_ORDERS', 'viewOther');
    if (viewOther) return {};
    return {
      OR: [
        { assignees: { some: { id: user.userId } } },
        { createdById: user.userId },
      ],
    };
  }
}
