import { WMSLayerConfig } from "@/app/(modules)/maps/state/active-layers";

import { LatLngBoundsExpression } from "leaflet";

interface GetLegendUrlOptions {
  baseUrl: string;
  layerName: string;
  format?: string;
  width?: number;
  height?: number;
  version?: string;
  sld_version?: string;
}

export const parseWmsUrl = (url: string | undefined | null) => {
  if (!url) return null;

  try {
    const [rawBaseUrl] = url.split("?");
    const parsedUrl = new URL(url);
    const params = parsedUrl.searchParams;

    const bbox = params.get("bbox");
    return {
      baseUrl: rawBaseUrl,
      params: {
        service: params.get("service") ?? "WMS",
        request: "GetMap",
        version: params.get("version") ?? "1.1.1",
        layers: params.get("layers") ?? "",
        styles: params.get("styles") ?? "",
        format: params.get("format") ?? "image/png",
        bounds: bbox ? parseBBoxToBounds(bbox) : null,
        transparent: "true",
        srs: params.get("srs") ?? params.get("crs") ?? "EPSG:4326",
      },
    };
  } catch (error) {
    console.error("Invalid WMS URL:", error);
    return null;
  }
};

export const constructWfsUrl = (wmsConfig: WMSLayerConfig) => {
  const { url, layers } = wmsConfig;

  if (!url) return;
  // Replace 'wms' with 'ows' in the base URL
  const baseUrl = url.replace("/wms", "/ows");

  // Construct query parameters
  const params = {
    service: "WFS",
    version: "1.0.0",
    request: "GetFeature",
    typeName: layers,
    maxFeatures: 50,
    outputFormat: "application/json",
  };
  const queryString = Object.entries(params)
    .filter(([value]) => value !== undefined)
    .map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
    .join("&");

  return `${baseUrl}?${queryString}`;
};

export const getWmsTileLayerUrl = (url: string | undefined | null) => {
  const parsed = parseWmsUrl(url);
  if (!parsed) return null;

  const { baseUrl, params } = parsed;
  const version = params.version;
  const crsParamKey = version === "1.3.0" ? "crs" : "srs";

  const wmsParams = new URLSearchParams({
    service: params.service,
    request: params.request,
    version: version,
    layers: params.layers,
    styles: params.styles,
    format: "image/png",
    transparent: params.transparent,
    [crsParamKey]: params.srs,
  });

  return `${baseUrl}?${wmsParams.toString()}`;
};

export function parseBBoxToBounds(
  bboxString: string
): LatLngBoundsExpression | null {
  try {
    if (!bboxString) return null;

    const [minX, minY, maxX, maxY] = bboxString.split(",").map(Number);
    if ([minX, minY, maxX, maxY].some(isNaN)) return null;

    const bounds: LatLngBoundsExpression = [
      [minY, minX],
      [maxY, maxX],
    ];

    return bounds;
  } catch (e) {
    console.error("Invalid bbox input:", e);
    return null;
  }
}

export const getLegendUrl = ({
  baseUrl,
  layerName,
  format = "image/png",
  width,
  height,
  version = "1.1.1",
  sld_version = "1.1.0",
}: GetLegendUrlOptions): string => {
  const url = new URL(baseUrl);
  url.searchParams.set("SERVICE", "WMS");
  url.searchParams.set("VERSION", version);
  url.searchParams.set("REQUEST", "GetLegendGraphic");
  url.searchParams.set("FORMAT", format);
  url.searchParams.set("LAYER", layerName);
  url.searchParams.set("SLD_VERSION", sld_version);

  if (width) url.searchParams.set("WIDTH", width.toString());
  if (height) url.searchParams.set("HEIGHT", height.toString());

  return url.toString();
};

export const getWmsLayerBounds = async (
  baseUrl: string,
  layerName: string
): Promise<L.LatLngBoundsExpression | undefined> => {
  try {
    const capabilitiesUrl =
      `${baseUrl}?` +
      new URLSearchParams({
        service: "WMS",
        version: "1.3.0",
        request: "GetCapabilities",
      });

    const response = await fetch(capabilitiesUrl);
    if (!response.ok) return undefined;

    const text = await response.text();
    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(text, "text/xml");

    // Find the requested layer in capabilities
    const layerElements = xmlDoc.getElementsByTagName("Layer");

    for (const element of layerElements) {
      const nameElement = element.getElementsByTagName("Name")[0];
      if (nameElement && nameElement.textContent === layerName) {
        const bboxElement =
          element.getElementsByTagName("BoundingBox")[0] ||
          element.getElementsByTagName("EX_GeographicBoundingBox")[0];

        if (bboxElement) {
          // Extract bounds based on the bbox type
          if (bboxElement.tagName === "BoundingBox") {
            const minx = parseFloat(bboxElement.getAttribute("minx") ?? "0");
            const miny = parseFloat(bboxElement.getAttribute("miny") ?? "0");
            const maxx = parseFloat(bboxElement.getAttribute("maxx") ?? "0");
            const maxy = parseFloat(bboxElement.getAttribute("maxy") ?? "0");
            return [
              [miny, minx],
              [maxy, maxx],
            ];
          } else {
            const westBound = parseFloat(
              bboxElement.getElementsByTagName("westBoundLongitude")[0]
                ?.textContent ?? "0"
            );
            const southBound = parseFloat(
              bboxElement.getElementsByTagName("southBoundLatitude")[0]
                ?.textContent ?? "0"
            );
            const eastBound = parseFloat(
              bboxElement.getElementsByTagName("eastBoundLongitude")[0]
                ?.textContent ?? "0"
            );
            const northBound = parseFloat(
              bboxElement.getElementsByTagName("northBoundLatitude")[0]
                ?.textContent ?? "0"
            );
            return [
              [southBound, westBound],
              [northBound, eastBound],
            ];
          }
        }
      }
    }

    return undefined;
  } catch (error) {
    console.error("Error fetching layer bounds:", error);
    return undefined;
  }
};

export const getWmsToGeoJsonUrl = (wmsUrl: string | undefined | null) => {
  if (!wmsUrl) return null;

  const parsedWms = parseWmsUrl(wmsUrl);
  if (!parsedWms) return null;

  const { baseUrl, params } = parsedWms;
  const downloadUrl = new URL(baseUrl);

  // Set parameters for GeoJSON download
  downloadUrl.searchParams.set("service", "WFS");
  downloadUrl.searchParams.set("version", "1.0.0");
  downloadUrl.searchParams.set("request", "GetFeature");
  downloadUrl.searchParams.set("typeName", params.layers);
  downloadUrl.searchParams.set("outputFormat", "application/json");
  downloadUrl.searchParams.set("srsName", params.srs);

  return downloadUrl.toString();
};

export const getWmsToShpUrl = (wmsUrl: string | undefined | null) => {
  if (!wmsUrl) return null;

  const parsedWms = parseWmsUrl(wmsUrl);
  if (!parsedWms) return null;

  const { baseUrl, params } = parsedWms;
  const downloadUrl = new URL(baseUrl);

  downloadUrl.searchParams.set("service", "WFS");
  downloadUrl.searchParams.set("version", "1.0.0");
  downloadUrl.searchParams.set("request", "GetFeature");
  downloadUrl.searchParams.set("typeName", params.layers);
  downloadUrl.searchParams.set("outputFormat", "SHAPE-ZIP");
  downloadUrl.searchParams.set("srsName", params.srs);

  return downloadUrl.toString();
};

export const getWmsToKmlUrl = (wmsUrl: string | undefined | null) => {
  if (!wmsUrl) return null;

  const parsedWms = parseWmsUrl(wmsUrl);
  if (!parsedWms) return null;

  const { baseUrl, params } = parsedWms;
  const downloadUrl = new URL(baseUrl);

  downloadUrl.searchParams.set("service", "WFS");
  downloadUrl.searchParams.set("version", "1.0.0");
  downloadUrl.searchParams.set("request", "GetFeature");
  downloadUrl.searchParams.set("typeName", params.layers);
  downloadUrl.searchParams.set(
    "outputFormat",
    "application/vnd.google-earth.kml+xml"
  );
  downloadUrl.searchParams.set("srsName", params.srs);

  return downloadUrl.toString();
};
