import type { Map as MapLibreMap, PaddingOptions } from "maplibre-gl";
import { getWorkbenchCameraPadding } from "../layout/workbench-layout";
import { WATER_NETWORK_GLOBAL_VIEW } from "./sources";

type LngLatBounds = [[number, number], [number, number]];
type WebMercatorBbox = readonly [number, number, number, number];

export type NetworkViewParams = {
  bbox3857: WebMercatorBbox;
};

const WEB_MERCATOR_RADIUS = 6378137;
const GLOBAL_VIEW_MAX_ZOOM = 13;
const GLOBAL_VIEW_PADDING: PaddingOptions = { top: 96, right: 48, bottom: 48, left: 48 };
export function getWorkbenchPadding(
  leftPanelOpen: boolean,
  rightPanelOpen: boolean,
  conditionPanelExpanded = false,
  agentPanelWidth?: number
): PaddingOptions {
  return getWorkbenchCameraPadding(1280, {
    agentOpen: leftPanelOpen,
    agentWidth: agentPanelWidth,
    conditionOpen: rightPanelOpen,
    conditionExpanded: conditionPanelExpanded
  });
}

export function getResponsiveWorkbenchPadding(
  map: MapLibreMap,
  leftPanelOpen: boolean,
  rightPanelOpen: boolean,
  conditionPanelExpanded = false,
  agentPanelWidth?: number
): PaddingOptions {
  const width = getMapViewportSize(map).width;
  return getResponsivePadding(
    map,
    getWorkbenchCameraPadding(width, {
      agentOpen: leftPanelOpen,
      agentWidth: agentPanelWidth,
      conditionOpen: rightPanelOpen,
      conditionExpanded: conditionPanelExpanded
    })
  );
}

export function fitNetworkBounds(
  map: MapLibreMap,
  viewParams: NetworkViewParams = WATER_NETWORK_GLOBAL_VIEW,
  padding: PaddingOptions = GLOBAL_VIEW_PADDING
) {
  map.fitBounds(getLngLatBoundsFromBbox3857(viewParams.bbox3857), {
    padding: getResponsivePadding(map, padding),
    maxZoom: GLOBAL_VIEW_MAX_ZOOM,
    duration: 600
  });
}

export function getResponsivePadding(map: MapLibreMap, padding: PaddingOptions): PaddingOptions {
  const { width, height } = getMapViewportSize(map);
  const horizontalPadding = padding.left + padding.right;
  const verticalPadding = padding.top + padding.bottom;
  const maxHorizontalPadding = width * 0.58;
  const maxVerticalPadding = height * 0.58;
  const horizontalScale = horizontalPadding > 0 ? maxHorizontalPadding / horizontalPadding : 1;
  const verticalScale = verticalPadding > 0 ? maxVerticalPadding / verticalPadding : 1;
  const scale = Math.min(1, horizontalScale, verticalScale);

  if (scale >= 1) {
    return padding;
  }

  return {
    top: Math.round(padding.top * scale),
    right: Math.round(padding.right * scale),
    bottom: Math.round(padding.bottom * scale),
    left: Math.round(padding.left * scale)
  };
}

function getMapViewportSize(map: MapLibreMap) {
  const canvas = map.getCanvas();
  return {
    width: canvas.clientWidth || canvas.width,
    height: canvas.clientHeight || canvas.height
  };
}

function getLngLatBoundsFromBbox3857(bbox3857: WebMercatorBbox): LngLatBounds {
  const [minX, minY, maxX, maxY] = bbox3857;

  return [webMercatorToLngLat(minX, minY), webMercatorToLngLat(maxX, maxY)];
}

function webMercatorToLngLat(x: number, y: number): [number, number] {
  const lng = (x / WEB_MERCATOR_RADIUS) * (180 / Math.PI);
  const lat = (2 * Math.atan(Math.exp(y / WEB_MERCATOR_RADIUS)) - Math.PI / 2) * (180 / Math.PI);

  return [lng, lat];
}
