import type { ExpressionSpecification, StyleSpecification } from "maplibre-gl";
import { MAP_STYLE_TOKENS } from "./map-colors";
import { SUPPLY_LAYER_VISUALS } from "./map-layer-visuals";
import { SCADA_HIT_LAYER_ID } from "./scada";
import { SOURCE_LAYERS } from "./sources";
import { SUPPLY_ASSET_IMAGE_IDS } from "./supply-icons";
import {
  createSupplyActiveOpacity,
  createSupplyStateColor,
  supplyFacilityOpacityExpression,
  supplyInactiveExpression,
  supplyIconOpacityExpression,
  supplyPipeColorExpression,
  supplyPipeDashExpression
} from "./supply-style-expressions";

type Layer = NonNullable<StyleSpecification["layers"]>[number];
type AuthoredLayer = { id: string; type: string; [key: string]: unknown };

export const INTERACTIVE_HIT_LAYER_IDS = [
  "pipes-hit",
  "junctions-hit",
  "valves-hit",
  "reservoirs-hit",
  SCADA_HIT_LAYER_ID
] as const;

const hoveredOpacity: ExpressionSpecification = [
  "case",
  [
    "all",
    ["boolean", ["feature-state", "hovered"], false],
    ["!", ["any", ["boolean", ["feature-state", "selected"], false], ["boolean", ["feature-state", "highlighted"], false]]]
  ],
  1,
  0
];

const selectedOpacity: ExpressionSpecification = [
  "case",
  ["any", ["boolean", ["feature-state", "selected"], false], ["boolean", ["feature-state", "highlighted"], false]],
  1,
  0
];

const opacityTransition = { duration: 120, delay: 0 } as const;
const layers: AuthoredLayer[] = [];

// Business layers: white casing or halo first, then the supply asset core.
layers.push(
  {
    id: "pipes-casing",
    type: "line",
    source: "pipes",
    "source-layer": SOURCE_LAYERS.pipes,
    minzoom: SUPPLY_LAYER_VISUALS.pipes.minzoom,
    paint: {
      "line-color": MAP_STYLE_TOKENS.canvas.casing,
      "line-width": SUPPLY_LAYER_VISUALS.pipes.casingWidth,
      "line-opacity": createSupplyActiveOpacity(0.86),
      "line-dasharray": supplyPipeDashExpression
    }
  },
  {
    id: "pipes-flow",
    type: "line",
    source: "pipes",
    "source-layer": SOURCE_LAYERS.pipes,
    minzoom: SUPPLY_LAYER_VISUALS.pipes.minzoom,
    paint: {
      "line-color": supplyPipeColorExpression,
      "line-width": SUPPLY_LAYER_VISUALS.pipes.width,
      "line-opacity": createSupplyActiveOpacity(0.92),
      "line-dasharray": supplyPipeDashExpression
    }
  },
  {
    id: "pipes-inactive-casing",
    type: "line",
    source: "pipes",
    "source-layer": SOURCE_LAYERS.pipes,
    minzoom: SUPPLY_LAYER_VISUALS.pipes.minzoom,
    filter: supplyInactiveExpression,
    paint: {
      "line-color": MAP_STYLE_TOKENS.canvas.casing,
      "line-width": SUPPLY_LAYER_VISUALS.pipes.casingWidth,
      "line-opacity": 0.86,
      "line-dasharray": [2, 1.5]
    }
  },
  {
    id: "pipes-inactive",
    type: "line",
    source: "pipes",
    "source-layer": SOURCE_LAYERS.pipes,
    minzoom: SUPPLY_LAYER_VISUALS.pipes.minzoom,
    filter: supplyInactiveExpression,
    paint: {
      "line-color": MAP_STYLE_TOKENS.state.inactive,
      "line-width": SUPPLY_LAYER_VISUALS.pipes.width,
      "line-opacity": 0.92,
      "line-dasharray": [2, 1.5]
    }
  },
  {
    id: "junctions-halo",
    type: "circle",
    source: "junctions",
    "source-layer": SOURCE_LAYERS.junctions,
    minzoom: SUPPLY_LAYER_VISUALS.junctions.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.canvas.casing,
      "circle-radius": SUPPLY_LAYER_VISUALS.junctions.haloRadius,
      "circle-opacity": SUPPLY_LAYER_VISUALS.junctions.haloOpacity,
      "circle-stroke-width": 0
    }
  },
  {
    id: "junctions",
    type: "circle",
    source: "junctions",
    "source-layer": SOURCE_LAYERS.junctions,
    minzoom: SUPPLY_LAYER_VISUALS.junctions.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.supply.junction,
      "circle-radius": SUPPLY_LAYER_VISUALS.junctions.radius,
      "circle-opacity": 0.9
    }
  },
  {
    id: "valves-casing",
    type: "circle",
    source: "valves",
    "source-layer": SOURCE_LAYERS.valves,
    minzoom: SUPPLY_LAYER_VISUALS.valves.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.canvas.casing,
      "circle-radius": SUPPLY_LAYER_VISUALS.valves.haloRadius,
      "circle-opacity": 0.94
    }
  },
  {
    id: "valves",
    type: "circle",
    source: "valves",
    "source-layer": SOURCE_LAYERS.valves,
    minzoom: SUPPLY_LAYER_VISUALS.valves.minzoom,
    paint: {
      "circle-color": createSupplyStateColor(MAP_STYLE_TOKENS.supply.valve),
      "circle-radius": SUPPLY_LAYER_VISUALS.valves.radius,
      "circle-opacity": supplyFacilityOpacityExpression,
      "circle-stroke-width": 0
    }
  },
  {
    id: "valves-symbol",
    type: "symbol",
    source: "valves",
    "source-layer": SOURCE_LAYERS.valves,
    minzoom: SUPPLY_LAYER_VISUALS.valves.minzoom,
    layout: {
      "icon-image": SUPPLY_ASSET_IMAGE_IDS.valve,
      "icon-size": SUPPLY_LAYER_VISUALS.valves.iconSize,
      "icon-allow-overlap": true,
      "icon-ignore-placement": true
    },
    paint: {
      "icon-opacity": supplyIconOpacityExpression
    }
  },
  {
    id: "reservoirs-halo",
    type: "circle",
    source: "reservoirs",
    "source-layer": SOURCE_LAYERS.reservoirs,
    minzoom: SUPPLY_LAYER_VISUALS.reservoirs.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.canvas.casing,
      "circle-radius": SUPPLY_LAYER_VISUALS.reservoirs.haloRadius,
      "circle-opacity": 0.94
    }
  },
  {
    id: "reservoirs",
    type: "circle",
    source: "reservoirs",
    "source-layer": SOURCE_LAYERS.reservoirs,
    minzoom: SUPPLY_LAYER_VISUALS.reservoirs.minzoom,
    paint: {
      "circle-color": createSupplyStateColor(MAP_STYLE_TOKENS.supply.reservoir),
      "circle-radius": SUPPLY_LAYER_VISUALS.reservoirs.radius,
      "circle-opacity": 0,
      "circle-stroke-width": 0
    }
  },
  {
    id: "reservoirs-symbol",
    type: "symbol",
    source: "reservoirs",
    "source-layer": SOURCE_LAYERS.reservoirs,
    minzoom: SUPPLY_LAYER_VISUALS.reservoirs.minzoom,
    layout: {
      "icon-image": SUPPLY_ASSET_IMAGE_IDS.reservoir,
      "icon-size": SUPPLY_LAYER_VISUALS.reservoirs.iconSize,
      "icon-allow-overlap": true,
      "icon-ignore-placement": true
    },
    paint: {
      "icon-opacity": supplyIconOpacityExpression
    }
  }
);

const lineInteractions = [
  ["pipes", "pipes", SOURCE_LAYERS.pipes, SUPPLY_LAYER_VISUALS.pipes, supplyPipeColorExpression, undefined]
] as const;

lineInteractions.forEach(([prefix, source, sourceLayer, visual, color, dash]) => {
  const hoverPaint: Record<string, unknown> = {
    "line-color": color,
    "line-width": visual.hoverWidth,
    "line-opacity": hoveredOpacity,
    "line-opacity-transition": opacityTransition
  };
  if (dash) hoverPaint["line-dasharray"] = dash;

  layers.push(
    {
      id: `${prefix}-hover-outline`,
      type: "line",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "line-color": MAP_STYLE_TOKENS.canvas.casing,
        "line-width": visual.hoverOutlineWidth,
        "line-opacity": hoveredOpacity,
        "line-opacity-transition": opacityTransition
      }
    },
    {
      id: `${prefix}-hover`,
      type: "line",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: hoverPaint
    },
    {
      id: `${prefix}-selected-outer`,
      type: "line",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "line-color": MAP_STYLE_TOKENS.canvas.casing,
        "line-gap-width": visual.selectedWidth,
        "line-width": 4.5,
        "line-opacity": selectedOpacity,
        "line-opacity-transition": opacityTransition
      }
    },
    {
      id: `${prefix}-selected-outline`,
      type: "line",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "line-color": MAP_STYLE_TOKENS.state.selected,
        "line-gap-width": visual.selectedWidth,
        "line-width": visual.selectedBorderWidth,
        "line-opacity": selectedOpacity,
        "line-opacity-transition": opacityTransition
      }
    },
    {
      id: `${prefix}-selected-separator`,
      type: "line",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "line-color": MAP_STYLE_TOKENS.canvas.casing,
        "line-gap-width": visual.selectedWidth,
        "line-width": 1,
        "line-offset": -1.5,
        "line-opacity": selectedOpacity,
        "line-opacity-transition": opacityTransition
      }
    }
  );
});

const pointInteractions = [
  ["junctions", "junctions", SOURCE_LAYERS.junctions, SUPPLY_LAYER_VISUALS.junctions, MAP_STYLE_TOKENS.supply.junction],
  ["valves", "valves", SOURCE_LAYERS.valves, SUPPLY_LAYER_VISUALS.valves, MAP_STYLE_TOKENS.supply.valve],
  ["reservoirs", "reservoirs", SOURCE_LAYERS.reservoirs, SUPPLY_LAYER_VISUALS.reservoirs, MAP_STYLE_TOKENS.supply.reservoir]
] as const;

pointInteractions.forEach(([prefix, source, sourceLayer, visual, color]) => {
  layers.push(
    {
      id: `${prefix}-hover`,
      type: "circle",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "circle-color": MAP_STYLE_TOKENS.canvas.transparent,
        "circle-radius": visual.hoverRadius,
        "circle-opacity": hoveredOpacity,
        "circle-stroke-color": color,
        "circle-stroke-width": 2,
        "circle-stroke-opacity": hoveredOpacity,
        "circle-opacity-transition": opacityTransition,
        "circle-stroke-opacity-transition": opacityTransition
      }
    },
    {
      id: `${prefix}-selected-outer`,
      type: "circle",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "circle-color": MAP_STYLE_TOKENS.canvas.transparent,
        "circle-radius": visual.selectedRadius,
        "circle-opacity": selectedOpacity,
        "circle-stroke-color": MAP_STYLE_TOKENS.canvas.casing,
        "circle-stroke-width": 5,
        "circle-stroke-opacity": selectedOpacity,
        "circle-opacity-transition": opacityTransition,
        "circle-stroke-opacity-transition": opacityTransition
      }
    },
    {
      id: `${prefix}-selected`,
      type: "circle",
      source,
      "source-layer": sourceLayer,
      minzoom: visual.minzoom,
      paint: {
        "circle-color": MAP_STYLE_TOKENS.canvas.transparent,
        "circle-radius": visual.selectedRadius,
        "circle-opacity": selectedOpacity,
        "circle-stroke-color": MAP_STYLE_TOKENS.state.selected,
        "circle-stroke-width": 2.5,
        "circle-stroke-opacity": selectedOpacity,
        "circle-opacity-transition": opacityTransition,
        "circle-stroke-opacity-transition": opacityTransition
      }
    }
  );
});

// Transparent hit layers stay last so pointer targeting is independent of paint order.
layers.push(
  {
    id: "pipes-hit",
    type: "line",
    source: "pipes",
    "source-layer": SOURCE_LAYERS.pipes,
    minzoom: SUPPLY_LAYER_VISUALS.pipes.minzoom,
    paint: {
      "line-color": MAP_STYLE_TOKENS.canvas.transparent,
      "line-width": SUPPLY_LAYER_VISUALS.pipes.hitWidth,
      "line-opacity": 0
    }
  },
  {
    id: "junctions-hit",
    type: "circle",
    source: "junctions",
    "source-layer": SOURCE_LAYERS.junctions,
    minzoom: SUPPLY_LAYER_VISUALS.junctions.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.canvas.transparent,
      "circle-radius": SUPPLY_LAYER_VISUALS.junctions.hitRadius,
      "circle-opacity": 0
    }
  },
  {
    id: "valves-hit",
    type: "circle",
    source: "valves",
    "source-layer": SOURCE_LAYERS.valves,
    minzoom: SUPPLY_LAYER_VISUALS.valves.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.canvas.transparent,
      "circle-radius": SUPPLY_LAYER_VISUALS.valves.hitRadius,
      "circle-opacity": 0
    }
  },
  {
    id: "reservoirs-hit",
    type: "circle",
    source: "reservoirs",
    "source-layer": SOURCE_LAYERS.reservoirs,
    minzoom: SUPPLY_LAYER_VISUALS.reservoirs.minzoom,
    paint: {
      "circle-color": MAP_STYLE_TOKENS.canvas.transparent,
      "circle-radius": SUPPLY_LAYER_VISUALS.reservoirs.hitRadius,
      "circle-opacity": 0
    }
  }
);

export const waterNetworkLayers: StyleSpecification["layers"] = layers as Layer[];

const interactionIndex = layers.findIndex((layer) => layer.id === "pipes-hover-outline");
const hitIndex = layers.findIndex((layer) => layer.id === INTERACTIVE_HIT_LAYER_IDS[0]);

export const waterNetworkBusinessLayers = layers.slice(0, interactionIndex) as Layer[];
export const waterNetworkInteractionLayers = layers.slice(interactionIndex, hitIndex) as Layer[];
export const waterNetworkHitLayers = layers.slice(hitIndex) as Layer[];
export const WORKBENCH_INTERACTION_BEFORE_ID = "pipes-hover-outline";
