'use client';

import { useEffect, useMemo, useState } from 'react';
import dynamic from 'next/dynamic';
import { AlertTriangle, MapPinned, RefreshCw } from 'lucide-react';

import { getDefectVisual } from '@/constants/defectVisualConfig';
import type {
  TicketAssignmentDetectionItem,
  TicketAssignmentDetectionsResponse,
} from '@/types';
import { Button } from '@/components/ui/button';
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from '@/components/ui/card';

import type { AssignmentRangePoint } from './assignmentRange';

type AssignmentIssuesMapProps = {
  data?: TicketAssignmentDetectionsResponse;
  isLoading: boolean;
  isError: boolean;
  startPoint: AssignmentRangePoint | null;
  endPoint: AssignmentRangePoint | null;
  onRetry: () => void;
};

type AssignmentMapItem = {
  id: number;
  class_name: string;
  display_name: string;
  latitude: number;
  longitude: number;
  sequence: number;
};

type AssignmentMapClass = {
  class_name: string;
  display_name: string;
};

type ClientAssignmentIssuesMapProps = {
  items: AssignmentMapItem[];
  classes: AssignmentMapClass[];
  selectedDetectionId: number | null;
  startPoint: AssignmentRangePoint | null;
  endPoint: AssignmentRangePoint | null;
  onSelectDetection: (detectionId: number) => void;
};

function toMapItem(
  item: TicketAssignmentDetectionItem,
  sequence: number,
): AssignmentMapItem | null {
  const { latitude, longitude } = item.location;
  if (
    typeof latitude !== 'number' ||
    typeof longitude !== 'number' ||
    !Number.isFinite(latitude) ||
    !Number.isFinite(longitude) ||
    latitude < -90 ||
    latitude > 90 ||
    longitude < -180 ||
    longitude > 180
  ) {
    return null;
  }

  return {
    id: item.detection.id,
    class_name: item.detection.class_name,
    display_name: item.detection.display_name,
    latitude,
    longitude,
    sequence,
  };
}

const ClientAssignmentIssuesMap = dynamic<ClientAssignmentIssuesMapProps>(
  async () => {
    const {
      CircleMarker,
      MapContainer,
      Polyline,
      Popup,
      TileLayer,
      Tooltip,
      useMap,
    } = await import('react-leaflet');
    const { canvas } = await import('leaflet');

    function FitMapToIssues({ items }: { items: AssignmentMapItem[] }) {
      const map = useMap();

      useEffect(() => {
        if (items.length === 0) return;

        if (items.length === 1) {
          map.setView([items[0].latitude, items[0].longitude], 17);
          return;
        }

        map.fitBounds(
          items.map(
            (item) => [item.latitude, item.longitude] as [number, number],
          ),
          {
            padding: [32, 32],
            maxZoom: 17,
          },
        );
      }, [items, map]);

      return null;
    }

    return function ClientAssignmentIssuesMapInner({
      items,
      classes,
      selectedDetectionId,
      startPoint,
      endPoint,
      onSelectDetection,
    }: ClientAssignmentIssuesMapProps) {
      const markerRenderer = useMemo(() => canvas({ tolerance: 12 }), []);
      const displayNames = new Map(
        classes.map((item) => [item.class_name, item.display_name]),
      );
      const initialCenter: [number, number] = [
        items[0].latitude,
        items[0].longitude,
      ];
      const orderedItems = [...items].sort(
        (first, second) =>
          first.sequence - second.sequence || first.id - second.id,
      );
      const startIndex = orderedItems.findIndex(
        (item) => item.id === startPoint?.detectionId,
      );
      const endIndex = orderedItems.findIndex(
        (item) => item.id === endPoint?.detectionId,
      );
      const hasSelectedRange = startIndex >= 0 && endIndex >= 0;
      const rangeItems = hasSelectedRange
        ? orderedItems.slice(
            Math.min(startIndex, endIndex),
            Math.max(startIndex, endIndex) + 1,
          )
        : [];
      const rangeDetectionIds = new Set(rangeItems.map((item) => item.id));
      const rangePath = rangeItems.map(
        (item) => [item.latitude, item.longitude] as [number, number],
      );

      return (
        <MapContainer
          center={initialCenter}
          zoom={15}
          scrollWheelZoom
          className="h-full w-full"
        >
          <TileLayer
            attribution="&copy; OpenStreetMap contributors"
            url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
          />
          <FitMapToIssues items={hasSelectedRange ? rangeItems : items} />

          {rangePath.length >= 2 ? (
            <>
              <Polyline
                positions={rangePath}
                interactive={false}
                pathOptions={{
                  color: '#3b82f6',
                  opacity: 0.18,
                  weight: 26,
                }}
              />
              <Polyline
                positions={rangePath}
                interactive={false}
                pathOptions={{
                  color: '#2563eb',
                  opacity: 0.9,
                  weight: 4,
                }}
              />
            </>
          ) : null}

          {items.map((item) => {
            const visual = getDefectVisual(item.class_name);
            const IssueIcon = visual.icon;
            const isSelected = selectedDetectionId === item.id;
            const isStart = startPoint?.detectionId === item.id;
            const isEnd = endPoint?.detectionId === item.id;
            const rangeLabel = isStart ? 'Start' : isEnd ? 'End' : null;
            const rangeColor = isStart
              ? '#16a34a'
              : isEnd
                ? '#dc2626'
                : visual.boundingBoxColor;
            const isRangeIssue = isStart || isEnd;
            const isInsideRange = rangeDetectionIds.has(item.id);
            const displayName =
              displayNames.get(item.class_name) ??
              item.display_name ??
              item.class_name.replaceAll('_', ' ');
            return (
              <CircleMarker
                key={item.id}
                center={[item.latitude, item.longitude]}
                renderer={markerRenderer}
                radius={isSelected || isRangeIssue ? 11 : isInsideRange ? 9 : 7}
                pathOptions={{
                  color:
                    isSelected || isRangeIssue
                      ? '#ffffff'
                      : visual.boundingBoxColor,
                  fillColor: rangeColor,
                  fillOpacity:
                    isSelected || isRangeIssue
                      ? 1
                      : hasSelectedRange && !isInsideRange
                        ? 0.22
                        : 0.82,
                  opacity: hasSelectedRange && !isInsideRange ? 0.3 : 1,
                  weight: isSelected || isRangeIssue ? 4 : 2,
                }}
                eventHandlers={{
                  click: () => onSelectDetection(item.id),
                }}
              >
                <Popup minWidth={240} maxWidth={280}>
                  <div className="w-60">
                    <div className="flex items-start gap-3 border-b pb-3 pr-4">
                      <span
                        className="flex size-9 shrink-0 items-center justify-center rounded-full"
                        style={{
                          backgroundColor: `${visual.boundingBoxColor}1f`,
                          color: visual.boundingBoxColor,
                        }}
                      >
                        <IssueIcon className="size-4" />
                      </span>
                      <div className="min-w-0 flex-1">
                        <p className="!m-0 truncate text-sm font-semibold leading-5">
                          {displayName}
                        </p>
                        <p className="!m-0 text-xs leading-4 text-muted-foreground">
                          Detection #{item.id}
                        </p>
                      </div>
                      {rangeLabel ? (
                        <span className="flex shrink-0 items-center gap-1.5 text-xs font-semibold">
                          <span
                            className="size-3 rounded-full"
                            style={{ backgroundColor: rangeColor }}
                          />
                          {rangeLabel}
                        </span>
                      ) : null}
                    </div>

                    <dl className="!m-0 space-y-2 py-3 text-sm">
                      <div className="flex items-center justify-between gap-4">
                        <dt className="text-muted-foreground">Latitude</dt>
                        <dd className="!m-0 font-mono text-xs tabular-nums">
                          {item.latitude.toFixed(6)}
                        </dd>
                      </div>
                      <div className="flex items-center justify-between gap-4">
                        <dt className="text-muted-foreground">Longitude</dt>
                        <dd className="!m-0 font-mono text-xs tabular-nums">
                          {item.longitude.toFixed(6)}
                        </dd>
                      </div>
                    </dl>
                  </div>
                </Popup>
                {rangeLabel ? (
                  <Tooltip
                    permanent
                    direction="top"
                    offset={[0, -10]}
                    opacity={1}
                    className="range-point-label"
                  >
                    <span style={{ color: rangeColor }}>{rangeLabel}</span>
                  </Tooltip>
                ) : null}
              </CircleMarker>
            );
          })}
        </MapContainer>
      );
    };
  },
  {
    ssr: false,
    loading: () => <div className="size-full animate-pulse bg-muted" />,
  },
);

export function AssignmentIssuesMap({
  data,
  isLoading,
  isError,
  startPoint,
  endPoint,
  onRetry,
}: AssignmentIssuesMapProps) {
  const [selectedDetectionId, setSelectedDetectionId] = useState<number | null>(
    null,
  );
  const validItems = useMemo(
    () =>
      (data?.items ?? []).flatMap((item, index) => {
        const mapItem = toMapItem(item, index);
        return mapItem ? [mapItem] : [];
      }),
    [data?.items],
  );
  const classes = useMemo(
    () =>
      Array.from(
        new Map(
          validItems.map((item) => [
            item.class_name,
            {
              class_name: item.class_name,
              display_name: item.display_name,
            },
          ]),
        ).values(),
      ),
    [validItems],
  );
  const pageItemCount = data?.items.length ?? 0;

  useEffect(() => {
    if (
      selectedDetectionId !== null &&
      !validItems.some((item) => item.id === selectedDetectionId)
    ) {
      setSelectedDetectionId(null);
    }
  }, [selectedDetectionId, validItems]);

  return (
    <Card className="overflow-hidden">
      <CardHeader className="border-b">
        <div className="flex items-start justify-between gap-4">
          <div className="flex items-start gap-3">
            <div className="rounded-lg bg-secondary p-2">
              <MapPinned className="size-5 text-primary" />
            </div>
            <div>
              <CardTitle className="text-base">Issues on Map</CardTitle>
              <CardDescription>
                Select Start and End in the table to preview the covered range.
              </CardDescription>
            </div>
          </div>
          {data ? (
            <span className="shrink-0 text-sm text-muted-foreground">
              {validItems.length} mapped
            </span>
          ) : null}
        </div>
      </CardHeader>

      <CardContent className="p-0">
        {isLoading ? (
          <div className="h-[440px] animate-pulse bg-muted" />
        ) : isError ? (
          <div className="flex min-h-72 flex-col items-center justify-center gap-3 px-6 text-center">
            <AlertTriangle className="size-7 text-destructive" />
            <div>
              <p className="font-medium">Unable to load issue locations.</p>
              <p className="mt-1 text-sm text-muted-foreground">
                Check the connection and try again.
              </p>
            </div>
            <Button type="button" variant="outline" onClick={onRetry}>
              <RefreshCw />
              Retry
            </Button>
          </div>
        ) : validItems.length === 0 ? (
          <div className="flex min-h-72 flex-col items-center justify-center px-6 text-center">
            <MapPinned className="size-7 text-muted-foreground" />
            <p className="mt-3 font-medium">No issue locations available.</p>
            <p className="mt-1 text-sm text-muted-foreground">
              Detections without valid coordinates cannot be placed on the map.
            </p>
          </div>
        ) : (
          <>
            <div className="h-[440px] min-w-0 bg-muted">
              <ClientAssignmentIssuesMap
                items={validItems}
                classes={classes}
                selectedDetectionId={selectedDetectionId}
                startPoint={startPoint}
                endPoint={endPoint}
                onSelectDetection={setSelectedDetectionId}
              />
            </div>

            <div
              className="flex flex-wrap items-center gap-x-4 gap-y-2 border-t px-4 py-3"
              aria-label="Map legend"
            >
              <span className="text-sm font-medium">Map legend</span>
              {classes.map((item) => {
                const visual = getDefectVisual(item.class_name);
                const IssueIcon = visual.icon;

                return (
                  <div
                    key={item.class_name}
                    className="flex items-center gap-2 text-sm text-muted-foreground"
                  >
                    <span
                      className="flex size-6 items-center justify-center rounded-full"
                      style={{
                        backgroundColor: `${visual.boundingBoxColor}1f`,
                        color: visual.boundingBoxColor,
                      }}
                    >
                      <IssueIcon className="size-3.5" />
                    </span>
                    <span>{item.display_name}</span>
                  </div>
                );
              })}
              {startPoint ? (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <span className="size-4 rounded-full border-2 border-white bg-green-600 shadow-sm" />
                  <span>Start</span>
                </div>
              ) : null}
              {endPoint ? (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <span className="size-4 rounded-full border-2 border-white bg-red-600 shadow-sm" />
                  <span>End</span>
                </div>
              ) : null}
              {startPoint && endPoint ? (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <span className="relative h-4 w-8 rounded-full bg-blue-500/20">
                    <span className="absolute top-1/2 right-0 left-0 h-0.5 -translate-y-1/2 bg-blue-600" />
                  </span>
                  <span>Coverage area</span>
                </div>
              ) : null}
            </div>

            <div className="border-t px-4 py-3">
              <span className="text-sm text-muted-foreground">
                {validItems.length} mapped from {pageItemCount} cached{' '}
                {pageItemCount === 1 ? 'issue' : 'issues'}
              </span>
            </div>
          </>
        )}
      </CardContent>
    </Card>
  );
}
