import type { MonitorViewport } from '../hooks/useMonitorData';

export const MONITOR_RETURN_PARAM = 'monitorReturn';

export type MonitorRouteContext = {
  mode: 'map' | 'list';
  keyword: string;
  protocol: string;
  status: string;
  selectedVin: string;
  detailOpen: boolean;
  viewport: MonitorViewport;
  listOffset: number;
  listLimit: number;
  hasViewport: boolean;
};

const DEFAULT_CONTEXT: MonitorRouteContext = {
  mode: 'map',
  keyword: '',
  protocol: '',
  status: '',
  selectedVin: '',
  detailOpen: false,
  viewport: { zoom: 5, bounds: '' },
  listOffset: 0,
  listLimit: 50,
  hasViewport: false
};

function integer(value: string | null, fallback: number, minimum: number, maximum: number) {
  const parsed = Number(value);
  return Number.isInteger(parsed) && parsed >= minimum && parsed <= maximum ? parsed : fallback;
}

function decimal(value: string | null, fallback: number, minimum: number, maximum: number) {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed >= minimum && parsed <= maximum ? parsed : fallback;
}

function validBounds(value: string | null) {
  if (!value) return '';
  const coordinates = value.split(',').map(Number);
  if (coordinates.length !== 4 || coordinates.some((coordinate) => !Number.isFinite(coordinate))) return '';
  const [west, south, east, north] = coordinates;
  if (west < -180 || east > 180 || south < -90 || north > 90 || west >= east || south >= north) return '';
  return coordinates.map((coordinate) => coordinate.toFixed(6)).join(',');
}

export function parseMonitorRouteContext(search: string | URLSearchParams): MonitorRouteContext {
  const params = typeof search === 'string' ? new URLSearchParams(search) : search;
  const selectedVin = params.get('selectedVin')?.trim().toUpperCase() ?? '';
  const bounds = validBounds(params.get('bounds'));
  const hasViewport = params.has('zoom') || Boolean(bounds);
  return {
    mode: params.get('mode') === 'list' ? 'list' : 'map',
    keyword: params.get('keyword')?.trim() ?? '',
    protocol: params.get('protocol')?.trim() ?? '',
    status: params.get('status')?.trim() ?? '',
    selectedVin,
    detailOpen: Boolean(selectedVin) && params.get('detail') !== 'collapsed',
    viewport: {
      zoom: decimal(params.get('zoom'), DEFAULT_CONTEXT.viewport.zoom, 3, 20),
      bounds
    },
    listOffset: integer(params.get('listOffset'), 0, 0, 1_000_000),
    listLimit: [20, 50, 100].includes(Number(params.get('listLimit'))) ? Number(params.get('listLimit')) : 50,
    hasViewport
  };
}

export function buildMonitorPath(context: Omit<MonitorRouteContext, 'hasViewport'> | MonitorRouteContext) {
  const params = new URLSearchParams();
  if (context.mode !== 'map') params.set('mode', context.mode);
  if (context.keyword.trim()) params.set('keyword', context.keyword.trim());
  if (context.protocol) params.set('protocol', context.protocol);
  if (context.status) params.set('status', context.status);
  if (context.selectedVin) {
    params.set('selectedVin', context.selectedVin);
    params.set('detail', context.detailOpen ? 'open' : 'collapsed');
  }
  params.set('zoom', String(Math.round(context.viewport.zoom * 100) / 100));
  if (validBounds(context.viewport.bounds)) params.set('bounds', validBounds(context.viewport.bounds));
  if (context.listOffset) params.set('listOffset', String(context.listOffset));
  if (context.listLimit !== 50) params.set('listLimit', String(context.listLimit));
  const query = params.toString();
  return query ? `/monitor?${query}` : '/monitor';
}

export function validatedMonitorReturn(value?: string | null) {
  if (!value) return '';
  try {
    const parsed = new URL(value, 'https://vehicle-platform.invalid');
    if (parsed.origin !== 'https://vehicle-platform.invalid' || parsed.pathname !== '/monitor') return '';
    return buildMonitorPath(parseMonitorRouteContext(parsed.searchParams));
  } catch {
    return '';
  }
}

export function withMonitorReturn(path: string, monitorPath: string) {
  const target = new URL(path, 'https://vehicle-platform.invalid');
  const validated = validatedMonitorReturn(monitorPath);
  if (validated) target.searchParams.set(MONITOR_RETURN_PARAM, validated);
  return `${target.pathname}${target.search}${target.hash}`;
}

export function monitorReturnFromParams(params: URLSearchParams) {
  return validatedMonitorReturn(params.get(MONITOR_RETURN_PARAM));
}

export function preserveMonitorReturn(params: URLSearchParams, monitorReturn: string) {
  if (monitorReturn) params.set(MONITOR_RETURN_PARAM, monitorReturn);
  else params.delete(MONITOR_RETURN_PARAM);
  return params;
}
