import { config } from "@/config/config";
import { along, lineString, length, toMercator } from "@turf/turf";
import type { FeatureLike } from "ol/Feature";
import MVT from "ol/format/MVT";
import { Point } from "ol/geom";
import type BaseLayer from "ol/layer/Base";
import VectorLayer from "ol/layer/Vector";
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
import { toLonLat } from "ol/proj";
import GeoJSON from "ol/format/GeoJSON";
import VectorSource from "ol/source/Vector";
import VectorTileSource from "ol/source/VectorTile";
import { Icon, Style } from "ol/style";
import type { FlatStyleLike } from "ol/style/flat";

import { markMapResourcePersistent } from "./mapLifecycle";

type MapExtent = [number, number, number, number];

type CreateOperationalMapSourcesOptions = {
  mapUrl: string;
  workspace: string;
};

type CreateOperationalLayersOptions = CreateOperationalMapSourcesOptions & {
  extent: MapExtent;
  persistent?: boolean;
  sources?: OperationalMapSources;
};

export type OperationalMapSources = {
  junctions: VectorTileSource;
  pipes: VectorTileSource;
  valves: VectorTileSource;
  reservoirs: VectorSource;
  pumps: VectorSource;
  tanks: VectorSource;
  scada: VectorSource;
};

const defaultFlatStyle = config.MAP_DEFAULT_STYLE as FlatStyleLike;
const valveStyle = {
  "icon-src": "/icons/valve.svg",
  "icon-scale": 0.1,
};

const createIconStyle = (src: string, scale = 0.1) =>
  new Style({ image: new Icon({ src, scale, anchor: [0.5, 0.5] }) });

const scadaStyle = (feature: FeatureLike) =>
  createIconStyle(
    feature.get("type") === "pipe_flow"
      ? "/icons/scada_flow.svg"
      : "/icons/scada_pressure.svg",
  );

const pumpStyle = (feature: FeatureLike) => {
  const geometry = feature.getGeometry();
  if (!geometry || geometry.getType() !== "LineString") return [];

  const coordinates = (geometry as any)
    .getCoordinates()
    .map((coordinate: number[]) => toLonLat(coordinate));
  if (coordinates.length < 2) return [];

  const featureLine = lineString(coordinates);
  const midpoint = along(featureLine, length(featureLine) / 2).geometry
    .coordinates;
  return [
    new Style({
      geometry: new Point(toMercator(midpoint)),
      image: new Icon({
        src: "/icons/pump.svg",
        scale: 0.12,
        anchor: [0.5, 0.5],
      }),
    }),
  ];
};

const pointProperties = [
  { name: "高程", value: "elevation" },
  { name: "实际需水量", value: "actual_demand" },
  { name: "水头", value: "total_head" },
  { name: "压力", value: "pressure" },
  { name: "水质", value: "quality" },
];

const pipeProperties = [
  { name: "管径", value: "diameter" },
  { name: "流量", value: "flow" },
  { name: "摩阻系数", value: "friction" },
  { name: "水头损失", value: "headloss" },
  { name: "单位水头损失", value: "unit_headloss" },
  { name: "水质", value: "quality" },
  { name: "反应速率", value: "reaction" },
  { name: "设置值", value: "setting" },
  { name: "状态", value: "status" },
  { name: "流速", value: "velocity" },
];

export const createOperationalMapSources = ({
  mapUrl,
  workspace,
}: CreateOperationalMapSourcesOptions): OperationalMapSources => {
  const vectorTileUrl = (name: string) =>
    `${mapUrl}/gwc/service/tms/1.0.0/${workspace}:${name}@WebMercatorQuad@pbf/{z}/{x}/{-y}.pbf`;
  const vectorUrl = (name: string) =>
    `${mapUrl}/${workspace}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=${workspace}:${name}&outputFormat=application/json`;

  return {
    junctions: new VectorTileSource({
      url: vectorTileUrl("geo_junctions"),
      format: new MVT(),
      projection: "EPSG:3857",
    }),
    pipes: new VectorTileSource({
      url: vectorTileUrl("geo_pipes"),
      format: new MVT(),
      projection: "EPSG:3857",
    }),
    valves: new VectorTileSource({
      url: vectorTileUrl("geo_valves"),
      format: new MVT(),
      projection: "EPSG:3857",
    }),
    reservoirs: new VectorSource({
      url: vectorUrl("geo_reservoirs"),
      format: new GeoJSON(),
    }),
    pumps: new VectorSource({
      url: vectorUrl("geo_pumps"),
      format: new GeoJSON(),
    }),
    tanks: new VectorSource({
      url: vectorUrl("geo_tanks"),
      format: new GeoJSON(),
    }),
    scada: new VectorSource({
      url: vectorUrl("geo_scada"),
      format: new GeoJSON(),
    }),
  };
};

export const createOperationalMapResources = ({
  mapUrl,
  workspace,
  extent,
  persistent = false,
  sources = createOperationalMapSources({ mapUrl, workspace }),
}: CreateOperationalLayersOptions) => {

  const layers = {
    junctions: new WebGLVectorTileLayer({
      source: sources.junctions as any,
      style: defaultFlatStyle,
      extent,
      maxZoom: 24,
      minZoom: 11,
      properties: {
        name: "节点",
        value: "junctions",
        type: "point",
        properties: pointProperties,
      },
    }),
    pipes: new WebGLVectorTileLayer({
      source: sources.pipes as any,
      style: defaultFlatStyle,
      extent,
      maxZoom: 24,
      minZoom: 11,
      properties: {
        name: "管道",
        value: "pipes",
        type: "linestring",
        properties: pipeProperties,
      },
    }),
    valves: new WebGLVectorTileLayer({
      source: sources.valves as any,
      style: valveStyle,
      extent,
      maxZoom: 24,
      minZoom: 16,
      properties: {
        name: "阀门",
        value: "valves",
        type: "linestring",
        properties: [],
      },
    }),
    reservoirs: new VectorLayer({
      source: sources.reservoirs,
      style: () => createIconStyle("/icons/reservior.svg"),
      extent,
      maxZoom: 24,
      minZoom: 11,
      properties: {
        name: "水库",
        value: "reservoirs",
        type: "point",
        properties: [],
      },
    }),
    pumps: new VectorLayer({
      source: sources.pumps,
      style: pumpStyle,
      extent,
      maxZoom: 24,
      minZoom: 11,
      properties: {
        name: "水泵",
        value: "pumps",
        type: "linestring",
        properties: [],
      },
    }),
    tanks: new VectorLayer({
      source: sources.tanks,
      style: () => createIconStyle("/icons/tank.svg"),
      extent,
      maxZoom: 24,
      minZoom: 11,
      properties: {
        name: "水箱",
        value: "tanks",
        type: "point",
        properties: [],
      },
    }),
    scada: new VectorLayer({
      source: sources.scada,
      style: scadaStyle,
      extent,
      maxZoom: 24,
      minZoom: 11,
      properties: {
        name: "SCADA",
        value: "scada",
        type: "point",
        properties: [],
      },
    }),
  };

  if (persistent) {
    Object.values(layers).forEach(markMapResourcePersistent);
  }

  const layerById = layers as Record<string, BaseLayer>;
  const enabledLayers = new Set(config.MAP_AVAILABLE_LAYERS);
  const orderedLayers = [
    "pipes",
    "tanks",
    "pumps",
    "reservoirs",
    "scada",
    "junctions",
    "valves",
  ]
    .filter((id) => enabledLayers.has(id))
    .map((id) => layerById[id]);

  return {
    sources,
    layers,
    orderedLayers,
    resetStyles: () => {
      layers.junctions.setStyle(defaultFlatStyle);
      layers.pipes.setStyle(defaultFlatStyle);
      layers.valves.setStyle(valveStyle);
    },
  };
};
