import { search as searchRepo, searchDeliveredLines } from '../repositories/search.repo.js';
import { findByEmployeeId as findStageTransitions } from '../repositories/employeeStageTransition.repo.js';
import { PERMISSIONS } from '../constants/permissions.js';

export interface SearchResultDto {
  orderId: string;
  orderNo: string;
  matchType: 'order_no' | 'so_number' | 'jo_no' | 'client';
  matchValue: string;
  orderProductId?: string;
}

export interface DeliveredLineSearchResultDto {
  orderId: string;
  orderNo: string;
  soNumber?: string;
  clientName: string;
  orderProductId: string;
  joNo?: string;
  productName: string;
  size?: string;
  dispatchedQuantity: number;
  alreadyReturnedQuantity: number;
  unitWeight: number;
}

export async function search(term: string, userId: string, userPermissions: string[]): Promise<SearchResultDto[]> {
  const trimmed = term.trim();
  if (trimmed.length < 2) return [];

  // Same "no rows means unrestricted" convention as advanceStage()'s own
  // stage-move check (order.service.ts) — an ORDERS_STAGE_UPDATE holder
  // with no assigned stage transitions can already move any line through
  // any stage, so there's nothing meaningful to scope their search to
  // either; only an employee actually narrowed to specific stages gets
  // results filtered down to them.
  const isUnrestricted =
    userPermissions.includes('*') ||
    userPermissions.includes(PERMISSIONS.ORDERS_VIEW) ||
    userPermissions.includes(PERMISSIONS.ORDERS_EDIT) ||
    userPermissions.includes(PERMISSIONS.REPORTS_VIEW);

  let relevantStageIds: string[] | null = null;
  if (!isUnrestricted) {
    const transitions = await findStageTransitions(userId);
    if (transitions.length > 0) {
      relevantStageIds = [...new Set(transitions.flatMap((t) => [t.fromStageId, t.toStageId]))];
    }
  }

  const rows = await searchRepo(trimmed, 10, relevantStageIds);
  return rows.map((r) => ({
    orderId: r.order_id,
    orderNo: r.order_no,
    matchType: r.match_type,
    matchValue: r.match_value,
    orderProductId: r.order_product_id,
  }));
}

// Scoped to delivered (fully dispatched, not cancelled) lines only — powers
// the Returned Orders request search box. No stage-restriction narrowing
// here (unlike search() above) since a delivered line has already left
// production, so an employee's stage assignments have nothing to do with
// whether they should be able to raise a return against it.
export async function searchDelivered(term: string): Promise<DeliveredLineSearchResultDto[]> {
  const trimmed = term.trim();
  if (trimmed.length < 2) return [];

  const rows = await searchDeliveredLines(trimmed, 10);
  return rows.map((r) => ({
    orderId: r.order_id,
    orderNo: r.order_no,
    soNumber: r.so_number,
    clientName: r.client_name,
    orderProductId: r.order_product_id,
    joNo: r.jo_no,
    productName: r.product_name,
    size: r.size,
    dispatchedQuantity: r.dispatched_quantity,
    alreadyReturnedQuantity: r.already_returned_quantity,
    unitWeight: r.unit_weight,
  }));
}
