"use client";
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @next/next/no-img-element */

import React, { useCallback, useEffect, useMemo, useState } from "react";
import Image from "next/image";
import { getLegendUrl } from "@/shared/utils/wms";
import { ActiveLayer } from "@/app/(modules)/maps/state/active-layers";
import { useAtomValue } from "jotai";
import { leafletLayerInstancesAtom } from "@/app/(modules)/maps/state/leaflet-layer-instances";
import L from "leaflet";
import { usePathname, useRouter, useSearchParams } from "next/navigation";

interface LegendDisplayProps {
  layer: ActiveLayer;
}

export const LegendDisplay = ({ layer }: LegendDisplayProps) => {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const instances = useAtomValue(leafletLayerInstancesAtom);

  const leafletInstance = useMemo(() => instances.get(layer.id), [instances, layer.id]);
  const filterKey = useMemo(() => `filter__${String(layer.id).replace(/[^a-zA-Z0-9_-]/g, "_")}`, [layer.id]);

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [rules, setRules] = useState<LegendRule[]>([]);
  const [attributeName, setAttributeName] = useState<string | null>(null);

  // Active values from URL (?filter=["FAIR","GOOD"]) or state
  const initialFilterFromUrl = useMemo(() => {
    try {
      // Prefer per-layer key; fallback to legacy "filter"
      const raw = searchParams?.get(filterKey) ?? searchParams?.get("filter");
      if (!raw) return [] as string[];
      const parsed = JSON.parse(raw);
      return Array.isArray(parsed) ? (parsed.filter((v) => typeof v === "string") as string[]) : [];
    } catch {
      return [] as string[];
    }
  }, [searchParams, filterKey]);

  const [activeValues, setActiveValues] = useState<string[]>(initialFilterFromUrl.slice(0, 1));

  // Build JSON legend URL and fetch
  useEffect(() => {
    const baseUrl = layer.layer.url;
    const layerName = layer.layer.layers;
    if (!baseUrl || !layerName) return;

    const legendJsonUrl = getLegendUrl({ baseUrl, layerName, format: "application/json" });
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        setError(null);
        const res = await fetch(legendJsonUrl);
        if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
        const json = (await res.json()) as GeoServerLegendJSON;
        if (cancelled) return;

        const parsedRules = extractRules(json);
        setRules(parsedRules);

        // Cache legend JSON for cross-component usage (e.g., highlight icon)
        try {
          const cacheKey = `legend-json:${baseUrl}:${layerName}`;
          sessionStorage.setItem(cacheKey, JSON.stringify(json));
        } catch {}

        const attr = deriveAttributeName(parsedRules);
        if (attr) setAttributeName(attr);
      } catch (e) {
        console.error("Failed to fetch legend JSON:", e);
        if (!cancelled) setError("Gagal memuat legenda");
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [layer.layer.url, layer.layer.layers]);

  useEffect(() => {
    setActiveValues(initialFilterFromUrl.slice(0, 1));
  }, [initialFilterFromUrl]);

  // Build and apply CQL_FILTER whenever activeValues change or instance/attribute available
  useEffect(() => {
    if (!leafletInstance || !(leafletInstance instanceof L.TileLayer.WMS)) return;
    let cql = "";
    if (activeValues.length === 1) {
      const pickedTitle = activeValues[0];
      const matched = findRuleByTitle(rules, pickedTitle);
      if (matched?.filter) {
        cql = normalizeFilter(matched.filter);
      } else {
        cql = buildCqlFilter(attributeName, activeValues);
      }
    }
    if (cql) {
      (leafletInstance as any).setParams({ CQL_FILTER: cql }, false);
    } else {
      if ((leafletInstance as any).wmsParams?.CQL_FILTER) {
        delete (leafletInstance as any).wmsParams.CQL_FILTER;
      }
      (leafletInstance as any).setParams({}, false);
    }
    (leafletInstance as any).redraw?.();
  }, [leafletInstance, attributeName, activeValues, rules]);

  const onToggle = useCallback((value: string) => {
    setActiveValues((prev) => {
      const isActive = prev.length === 1 && prev[0] === value;
      const next = isActive ? [] : [value];
      return next;
    });
  }, []);

  useEffect(() => {
    try {
      const params = new URLSearchParams(searchParams?.toString());
      const desired = activeValues.length === 1 ? JSON.stringify(activeValues) : null;
      const current = params.get(filterKey);
      if (desired && current !== desired) {
        params.set(filterKey, desired);
      } else if (!desired && current) {
        params.delete(filterKey);
      } else {
        return;
      }
      router.replace(`${pathname}?${params.toString()}`, { scroll: false });
    } catch {
      // ignore
    }
    // Only run when selection changes or location context changes
  }, [activeValues, pathname, router, searchParams, filterKey]);

  // If no WMS URL, fallback to previous image legend rendering
  if (!layer.layer.url || !layer.layer.layers) {
    return (
      <div className="pt-2 mt-2">
        <div className="text-xs text-gray-500">
          <p>No legend available</p>
        </div>
      </div>
    );
  }

  // Show image legend if JSON failed but URL exists
  if (error) {
    return (
      <div className="pt-2 mt-2">
        <div className="text-xs text-gray-500 space-y-2">
          <div className="text-zinc-500">{error}</div>
          <Image src={getLegendUrl({ baseUrl: layer.layer.url, layerName: layer.layer.layers ?? "", width: 200 })} alt={`${layer.name} legend`} width={200} height={40} className="w-auto h-auto max-w-full" unoptimized />
        </div>
      </div>
    );
  }

  return (
    <div className="pt-2 mt-2">
      <div className="text-xs text-gray-700">
        {loading && <div className="text-zinc-500">Memuat legenda…</div>}
        {!loading && rules.length === 0 && (
          <div className="text-zinc-600 space-y-2">
            <Image src={getLegendUrl({ baseUrl: layer.layer.url, layerName: layer.layer.layers ?? "", width: 200 })} alt={`${layer.name} legend`} width={200} height={40} className="w-auto h-auto max-w-full" unoptimized />
          </div>
        )}
        {!loading && rules.length > 0 && (
          <div className="flex flex-wrap gap-2">
            {rules.map((r, idx) => {
              const label = (r.title || r.name || "").trim() || `Rule ${idx + 1}`;
              const active = label && activeValues.length === 1 && activeValues[0] === label;
              const iconUrl = getRuleIcon(r);
              const color = iconUrl ? undefined : getRuleColor(r) || "#999999";
              return (
                <button
                  key={(r.title || r.name || label || idx.toString()) as string}
                  type="button"
                  onClick={() => onToggle(label)}
                  className={`cursor-pointer inline-flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${
                    active ? "bg-primary text-primary-foreground border-primary" : "bg-white text-zinc-800 border-zinc-300 hover:border-zinc-400"
                  }`}
                >
                  {iconUrl ? (
                    <img src={iconUrl} alt="" width={12} height={12} className="h-3 w-3 rounded-sm border border-black/10 object-contain bg-white" />
                  ) : (
                    <span className="inline-block h-3 w-3 rounded-sm border border-black/10" style={{ backgroundColor: color }} aria-hidden />
                  )}
                  <span className="whitespace-nowrap">{label || "(untitled)"}</span>
                </button>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
};

type LegendRule = {
  name?: string;
  title?: string;
  filter?: string;
  symbolizers?: unknown[];
  [k: string]: unknown;
};

type GeoServerLegendJSON =
  | {
      rules?: LegendRule[];
      [k: string]: unknown;
    }
  | {
      legend?: { rules?: LegendRule[] };
      [k: string]: unknown;
    };

function extractRules(json: GeoServerLegendJSON): LegendRule[] {
  // 1) { rules: [...] }
  if (Array.isArray((json as any)?.rules)) return ((json as any).rules ?? []) as LegendRule[];
  // 2) { legend: { rules: [...] } }
  if (Array.isArray((json as any)?.legend?.rules)) return ((json as any).legend.rules ?? []) as LegendRule[];
  // 3) GeoServer sometimes returns { Legend: { rules: [...] } }
  if (Array.isArray((json as any)?.Legend?.rules)) return ((json as any).Legend.rules ?? []) as LegendRule[];
  // 3b) Or { Legend: [ { rules: [...] }, ... ] }
  if (Array.isArray((json as any)?.Legend)) {
    const arr = (json as any).Legend as any[];
    const all = arr.flatMap((entry) => (Array.isArray(entry?.rules) ? entry.rules : []));
    if (all.length) return all as LegendRule[];
  }
  // 2b) Or { legend: [ { rules: [...] }, ... ] }
  if (Array.isArray((json as any)?.legend)) {
    const arr = (json as any).legend as any[];
    const all = arr.flatMap((entry) => (Array.isArray(entry?.rules) ? entry.rules : []));
    if (all.length) return all as LegendRule[];
  }
  // 4) Some variants: { classes: [{ name, title, symbolizers }] }
  if (Array.isArray((json as any)?.classes)) {
    return ((json as any).classes as any[]).map((c) => ({ name: c?.name, title: c?.title, symbolizers: c?.symbolizers, filter: c?.filter }));
  }
  return [];
}

function deriveAttributeName(rules: LegendRule[]): string | null {
  for (const r of rules) {
    const f = r.filter;
    if (f) {
      const trimmed = f.trim().replace(/^\[|\]$/g, "");
      let m = /^(\w+)\s*=\s*['"][^'"]+['"]/.exec(trimmed);
      if (m) return m[1];
      m = /^(\w+)\s*(?:[<>]=?|=)\s*['"][^'"]+['"]/.exec(trimmed);
      if (m) return m[1];
    }
    const t = (r.title || r.name || "").trim();
    if (t) {
      const m2 = /^(\w+)\s*=\s*['"][^'"]+['"]/.exec(t);
      if (m2) return m2[1];
    }
  }
  return null;
}

function buildCqlFilter(attribute: string | null, values: string[]): string | "" {
  if (!attribute || !values?.length) return "";
  const safeVals = values.map(quoteCql);
  return `${attribute} IN (${safeVals.join(",")})`;
}

function quoteCql(v: string) {
  const escaped = v.replace(/'/g, "''");
  return `'${escaped}'`;
}

function getRuleColor(rule: LegendRule): string | null {
  const sym = (rule as any)?.symbolizers;
  if (Array.isArray(sym) && sym.length) {
    for (const s of sym) {
      const poly = (s as any)?.polygonSymbolizer || (s as any)?.PolygonSymbolizer;
      const polyColor = poly?.fill?.color || poly?.Fill?.CssParameter || poly?.fill?.CssParameter;
      const foundPoly = pickColor(polyColor) || pickColor(poly?.fill) || pickColor(poly);
      if (foundPoly) return foundPoly;

      const point = (s as any)?.pointSymbolizer || (s as any)?.PointSymbolizer;
      const g = point?.graphic || point?.Graphic || point?.graphics;
      const foundPoint = pickColor(g?.fill) || pickColor(g?.stroke) || pickColor(g);
      if (foundPoint) return foundPoint;

      const line = (s as any)?.lineSymbolizer || (s as any)?.LineSymbolizer;
      const foundLine = pickColor(line?.stroke) || pickColor(line);
      if (foundLine) return foundLine;
    }
  }

  const any = deepFindColor(rule as any);
  return any;
}

function pickColor(node: any): string | null {
  if (!node) return null;
  if (typeof node === "string" && looksLikeColor(node)) return node;
  if (typeof node?.color === "string" && looksLikeColor(node.color)) return node.color;
  if (typeof node?.fill === "string" && looksLikeColor(node.fill)) return node.fill;
  if (typeof node?.stroke === "string" && looksLikeColor(node.stroke)) return node.stroke;
  if (typeof node?.css === "string" && looksLikeColor(node.css)) return node.css;
  if (typeof node?.["#text"] === "string" && looksLikeColor(node["#text"])) return node["#text"];
  return null;
}

function deepFindColor(obj: any, depth = 0): string | null {
  if (!obj || depth > 4) return null;
  const direct = pickColor(obj);
  if (direct) return direct;
  if (typeof obj === "object") {
    for (const k of Object.keys(obj)) {
      const c = deepFindColor(obj[k], depth + 1);
      if (c) return c;
    }
  }
  return null;
}

function looksLikeColor(s: string) {
  const t = s.trim();
  return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(t) || /^rgba?\(/.test(t) || /^hsla?\(/.test(t);
}

// getRuleValue removed as we now use labels directly

function findRuleByTitle(rules: LegendRule[], title: string | null | undefined): LegendRule | undefined {
  if (!title) return undefined;
  const key = title.trim();
  return rules.find((r) => (r.title || r.name || "").trim() === key);
}

function normalizeFilter(expr: string): string {
  let s = expr.trim();
  if (s.startsWith("[") && s.endsWith("]")) {
    s = s.slice(1, -1).trim();
  }
  if (!/^\(.*\)$/.test(s)) {
    s = `(${s})`;
  }
  return s;
}

// icon URL helpers
function getRuleIcon(rule: LegendRule): string | null {
  const sym = (rule as any)?.symbolizers;
  if (Array.isArray(sym) && sym.length) {
    for (const s of sym) {
      const point = (s as any)?.pointSymbolizer || (s as any)?.PointSymbolizer || (s as any)?.Point;
      const g = point?.graphic || point?.Graphic || point?.graphics || point;
      const url = deepFindUrl(g) || deepFindUrl(point) || deepFindUrl(s);
      if (url) return url;
    }
  }
  return deepFindUrl(rule as any);
}

function deepFindUrl(obj: any, depth = 0): string | null {
  if (!obj || depth > 4) return null;
  const direct = pickUrl(obj);
  if (direct) return direct;
  if (typeof obj === "object") {
    for (const k of Object.keys(obj)) {
      const v = (obj as any)[k];
      const u = deepFindUrl(v, depth + 1);
      if (u) return u;
    }
  }
  return null;
}

function pickUrl(node: any): string | null {
  if (!node) return null;
  // typical fields
  const candidates: unknown[] = [node.url, node.href, node.externalGraphic, node["xlink:href"], node["@xlink:href"]];
  for (const c of candidates) {
    if (typeof c === "string" && looksLikeUrl(c)) return c;
  }
  if (typeof node === "string" && looksLikeUrl(node)) return node;
  return null;
}

function looksLikeUrl(s: string): boolean {
  const t = s.trim();
  if (/^(data:)/i.test(t)) return true;
  if (/^https?:\/\//i.test(t)) return true;
  if (/\.(svg|png|jpg|jpeg|gif)(\?|#|$)/i.test(t)) return true;
  return false;
}
