import type { FeatureFilters, FeatureMeta } from '../types';

export const SPECIFIC_CRIMES_FILTER_NAME = 'Specific crimes';
export const SPECIFIC_CRIMES_FILTER_KEY_PREFIX = `${SPECIFIC_CRIMES_FILTER_NAME}:`;

export const SPECIFIC_CRIME_FEATURE_NAMES = [
  'Violence and sexual offences (avg/yr)',
  'Burglary (avg/yr)',
  'Robbery (avg/yr)',
  'Vehicle crime (avg/yr)',
  'Anti-social behaviour (avg/yr)',
  'Criminal damage and arson (avg/yr)',
  'Other theft (avg/yr)',
  'Theft from the person (avg/yr)',
  'Shoplifting (avg/yr)',
  'Bicycle theft (avg/yr)',
  'Drugs (avg/yr)',
  'Possession of weapons (avg/yr)',
  'Public order (avg/yr)',
  'Other crime (avg/yr)',
] as const;

const SPECIFIC_CRIME_FEATURE_NAME_SET = new Set<string>(SPECIFIC_CRIME_FEATURE_NAMES);

export function isSpecificCrimeFeatureName(name: string): boolean {
  return SPECIFIC_CRIME_FEATURE_NAME_SET.has(name);
}

export function isSpecificCrimeFilterName(name: string): boolean {
  return isSpecificCrimeFeatureName(name) || name.startsWith(SPECIFIC_CRIMES_FILTER_KEY_PREFIX);
}

export function createSpecificCrimeFilterKey(featureName: string, id: number | string): string {
  return `${SPECIFIC_CRIMES_FILTER_KEY_PREFIX}${encodeURIComponent(featureName)}:${id}`;
}

export function getSpecificCrimeFilterKeyId(name: string): string | null {
  if (!name.startsWith(SPECIFIC_CRIMES_FILTER_KEY_PREFIX)) return null;
  const rest = name.substring(SPECIFIC_CRIMES_FILTER_KEY_PREFIX.length);
  const lastColon = rest.lastIndexOf(':');
  return lastColon === -1 ? null : rest.substring(lastColon + 1);
}

export function parseSpecificCrimeFilterKey(name: string): string | null {
  if (!name.startsWith(SPECIFIC_CRIMES_FILTER_KEY_PREFIX)) return null;
  const rest = name.substring(SPECIFIC_CRIMES_FILTER_KEY_PREFIX.length);
  const lastColon = rest.lastIndexOf(':');
  if (lastColon === -1) return null;

  const decoded = decodeURIComponent(rest.substring(0, lastColon));
  return isSpecificCrimeFeatureName(decoded) ? decoded : null;
}

export function getSpecificCrimeFeatureName(name: string): string | null {
  if (isSpecificCrimeFeatureName(name)) return name;
  return parseSpecificCrimeFilterKey(name);
}

export function replaceSpecificCrimeFilterKeySelection(key: string, featureName: string): string {
  const id = getSpecificCrimeFilterKeyId(key) ?? '0';
  return createSpecificCrimeFilterKey(featureName, id);
}

export function getDefaultSpecificCrimeFeatureName(features: FeatureMeta[]): string | null {
  return (
    SPECIFIC_CRIME_FEATURE_NAMES.find((name) =>
      features.some((feature) => feature.name === name)
    ) ?? null
  );
}

export function normalizeSpecificCrimeFilters(filters: FeatureFilters): FeatureFilters {
  let changed = false;
  const next: FeatureFilters = {};

  for (const [name, value] of Object.entries(filters)) {
    if (isSpecificCrimeFeatureName(name)) {
      next[createSpecificCrimeFilterKey(name, Object.keys(next).length)] = value;
      changed = true;
      continue;
    }
    next[name] = value;
  }

  return changed ? next : filters;
}

export function getSpecificCrimeFilterMeta(features: FeatureMeta[]): FeatureMeta {
  const sourceFeatureName = getDefaultSpecificCrimeFeatureName(features);
  const sourceFeature = sourceFeatureName
    ? features.find((feature) => feature.name === sourceFeatureName)
    : undefined;

  return {
    name: SPECIFIC_CRIMES_FILTER_NAME,
    type: 'numeric',
    group: 'Crime',
    min: sourceFeature?.min ?? 0,
    max: sourceFeature?.max ?? 100,
    step: 1,
    description:
      'Violence, burglary, robbery, drugs, shoplifting, vehicle crime, anti-social behaviour, public order, theft, and other crime types',
    detail:
      'Filter by one street-level crime category at a time using an area-normalised crime density near each postcode (not a count of incidents per year).',
    source: 'crime',
    suffix: '',
  };
}

export function clampSpecificCrimeRange(
  value: [number, number],
  feature?: FeatureMeta
): [number, number] {
  const min = feature?.histogram?.min ?? feature?.min ?? 0;
  const max = feature?.histogram?.max ?? feature?.max ?? Math.max(1, value[1]);
  return [Math.max(min, Math.min(value[0], max)), Math.max(min, Math.min(value[1], max))];
}
