import { ProviderProps, ProvidersApiResponse, ScanProps } from "@/types";
import { ProviderGroup } from "@/types/components";
import { FilterEntity } from "@/types/filters";
import {
  getProviderDisplayName,
  GroupFilterEntity,
  ProviderConnectionStatus,
} from "@/types/providers";
import { ScanEntity } from "@/types/scans";

/**
 * Extracts normalized filters and search query from the URL search params.
 * Used Server Side Rendering (SSR). There is a hook (useUrlFilters) for client side.
 */
export const extractFiltersAndQuery = (
  searchParams: Record<string, unknown>,
) => {
  const filters: Record<string, string> = {
    ...Object.fromEntries(
      Object.entries(searchParams)
        .filter(([key]) => key.startsWith("filter["))
        .map(([key, value]) => [
          key,
          Array.isArray(value) ? value.join(",") : value?.toString() || "",
        ]),
    ),
  };

  const query = filters["filter[search]"] || "";
  return { filters, query };
};

/**
 * Returns true if there are any scan or inserted_at filters in the search params.
 * Used to determine whether to call the full findings endpoint.
 */
export const hasDateOrScanFilter = (searchParams: Record<string, unknown>) =>
  Object.keys(searchParams).some(
    (key) =>
      key.includes("inserted_at") ||
      key.includes("scan__in") ||
      key === "filter[scan]",
  );

/**
 * Returns true when finding views must use historical endpoints.
 * Scan filters are resolved to inserted_at server-side, but client drill-downs
 * still need to treat raw scan params as historical to stay aligned.
 */
export const hasHistoricalFindingFilter = (
  searchParams: Record<string, unknown>,
) => hasDateOrScanFilter(searchParams);

/**
 * Returns true when inserted_at filters are active.
 * Used by resources drill-down endpoints that support date scoping but not scan filters.
 */
export const hasDateFilter = (searchParams: Record<string, unknown>) =>
  Object.keys(searchParams).some((key) => key.includes("inserted_at"));

/**
 * Encodes sort strings by removing leading "+" symbols.
 */
export const encodeSort = (sort?: string) => sort?.replace(/^\+/, "") || "";

/**
 * Extracts the sort string and the stable key to use in Suspense boundaries.
 */
export const extractSortAndKey = (searchParams: Record<string, unknown>) => {
  const searchParamsKey = JSON.stringify(searchParams || {});
  const rawSort = searchParams.sort?.toString();
  const encodedSort = encodeSort(rawSort);

  return { searchParamsKey, rawSort, encodedSort };
};

/**
 * Replaces a specific field name inside a filter-style key of an object.
 * @param obj - The input object with filter-style keys (e.g., { 'filter[inserted_at]': '2025-05-21' }).
 * @param oldField - The field name to be replaced (e.g., 'inserted_at').
 * @param newField - The field name to replace with (e.g., 'updated_at').
 * @returns A new object with the updated filter key if a match is found.
 */
export function replaceFieldKey(
  obj: Record<string, string>,
  oldField: string,
  newField: string,
): Record<string, string> {
  const fieldObj: Record<string, string> = {};

  for (const key in obj) {
    const match = key.match(/^filter\[(.+)\]$/);
    if (match && match[1] === oldField) {
      const newKey = `filter[${newField}]`;
      fieldObj[newKey] = obj[key];
    } else {
      fieldObj[key] = obj[key];
    }
  }

  return fieldObj;
}
export const isScanEntity = (entity: ScanEntity) => {
  return entity && entity.providerInfo && entity.attributes;
};

/**
 * Canonical human label for a scan entity: "{Provider name} - {scan name}".
 * Provider name comes from `getProviderDisplayName` (e.g. "AWS", "Google Cloud"),
 * never the account alias/uid — those identify the account, not the provider.
 * Shared by the findings filter chips and the multi-select trigger badge so
 * both surfaces stay in sync. Returns the provider name alone when the scan
 * name is empty, or the scan name alone if the provider type doesn't resolve.
 */
export function getScanEntityLabel(scan: ScanEntity): string {
  const providerLabel = getProviderDisplayName(scan.providerInfo.provider);
  const scanName = scan.attributes.name || "";

  if (providerLabel && scanName) return `${providerLabel} - ${scanName}`;
  return providerLabel || scanName;
}

/**
 * Resolves the display name for a provider group filter value, falling back to
 * the raw id when the group can't be resolved. Shared by the findings and
 * resources filter utils so their chips stay in sync.
 */
export function getProviderGroupDisplayValue(
  groupId: string,
  groups: ProviderGroup[],
): string {
  const group = groups.find((item) => item.id === groupId);
  return group?.attributes.name || groupId;
}

/**
 * Creates a scan details mapping for filters from completed scans.
 * Used to provide detailed information for scan filters in the UI.
 */
export const createScanDetailsMapping = (
  completedScans: ScanProps[],
  providersData?: ProvidersApiResponse,
) => {
  if (!completedScans || completedScans.length === 0) {
    return [];
  }

  const scanMappings = completedScans.map((scan: ScanProps) => {
    // Get provider info from providerInfo if available, or find from providers data
    let providerInfo = scan.providerInfo;

    if (!providerInfo && scan.relationships?.provider?.data?.id) {
      const provider = providersData?.data?.find(
        (p: ProviderProps) => p.id === scan.relationships.provider.data.id,
      );
      if (provider) {
        providerInfo = {
          provider: provider.attributes.provider,
          alias: provider.attributes.alias,
          uid: provider.attributes.uid,
        };
      }
    }

    return {
      [scan.id]: {
        id: scan.id,
        providerInfo: {
          provider: providerInfo?.provider || "aws",
          alias: providerInfo?.alias,
          uid: providerInfo?.uid,
        },
        attributes: {
          name: scan.attributes.name,
          completed_at: scan.attributes.completed_at,
        },
      },
    };
  });

  return scanMappings;
};

// Helper to check if entity is a ProviderConnectionStatus (simple label/value object)
export const isConnectionStatus = (
  entity: FilterEntity,
): entity is ProviderConnectionStatus => {
  return !!(entity && "label" in entity && "value" in entity);
};

// Helper to check if entity is a GroupFilterEntity (organization or account group)
export const isGroupFilterEntity = (
  entity: FilterEntity,
): entity is GroupFilterEntity => {
  return !!(
    entity &&
    "name" in entity &&
    !("provider" in entity) &&
    !("label" in entity)
  );
};

/**
 * Connection status mapping for provider filters.
 * Maps boolean string values to user-friendly labels.
 */
export const CONNECTION_STATUS_MAPPING: Array<{
  [key: string]: FilterEntity;
}> = [
  {
    true: { label: "Connected", value: "true" } as ProviderConnectionStatus,
  },
  {
    false: {
      label: "Disconnected",
      value: "false",
    } as ProviderConnectionStatus,
  },
];
