import { useEffect, useState } from 'react';
import { LatLng } from 'leaflet';
import { newGuid } from '../utils';
import MapView from './MapView';
import AltitudeDialog from './AltitudeDialog';
import './flightPlan.css';
import JsonEditorDialog from './JsonEditorDialog';
import { COORDINATE_PRECISION } from '../config';
import L from 'leaflet';
import LeftBoard from './LeftBoard';
import { actionModes } from '../constants/actionModes';
import { mockGetAirplaneParams } from '../services/AircraftService';
import { RotatePhoneIcon } from '../icons/PhoneIcon';
import { purposes } from '../constants/purposes';
import type { FlightPoint, CalculatedPointInfo, MapRectangle, AircraftParams, LatLngPosition } from '../types';

interface FlightPlanProps {
  initialRectangles?: MapRectangle[];
}

export function FlightPlan({ initialRectangles = [] }: FlightPlanProps) {
  const [aircraft, setAircraft] = useState<AircraftParams | null>(null);
  const [points, setPoints] = useState<FlightPoint[]>([]);
  const [calculatedPointInfo, setCalculatedPointInfo] = useState<CalculatedPointInfo[]>([]);
  const [currentPosition, setCurrentPosition] = useState<LatLngPosition>(new LatLng(47.242, 35.024));
  const [mapCenter, setMapCenter] = useState<LatLngPosition>(new LatLng(47.242, 35.024));
  const [rectangles, setRectangles] = useState<MapRectangle[]>(initialRectangles);
  const [openDialog, setOpenDialog] = useState(false);
  const [altitude, setAltitude] = useState(300);
  const [latitude, setLatitude] = useState<number>(parseFloat(new LatLng(47.242, 35.024).lat.toFixed(COORDINATE_PRECISION)));
  const [longitude, setLongitude] = useState<number>(parseFloat(new LatLng(47.242, 35.024).lng.toFixed(COORDINATE_PRECISION)));
  const [meta, setMeta] = useState<string[]>([purposes[0].value, purposes[1].value]);
  const [rectangleColor, setRectangleColor] = useState('red');
  const [pendingPosition, setPendingPosition] = useState<LatLngPosition | null>(null);
  const [insertIndex, setInsertIndex] = useState<number | null>(null);
  const [jsonDialogOpen, setJsonDialogOpen] = useState(false);
  const [jsonText, setJsonText] = useState('');
  const [initialAltitude, setInitialAltitude] = useState(1000);
  const [currentAltitude, setCurrentAltitude] = useState(initialAltitude);
  const [actionMode, setActionMode] = useState<string>(actionModes.points);

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        const pos = new LatLng(position.coords.latitude, position.coords.longitude);
        setCurrentPosition(pos);
        setMapCenter(pos);
      },
      () => {
        const pos = new LatLng(47.242, 35.024);
        setCurrentPosition(pos);
        setMapCenter(pos);
      }
    );

    mockGetAirplaneParams().then((air) => {
      setAircraft(air);
    });
  }, []);

  const handleDialogClose = () => {
    setOpenDialog(false);
    setPendingPosition(null);
    setInsertIndex(null);
  };

  const handleAltitudeSubmit = () => {
    if (pendingPosition !== null) {
      const newPoint: FlightPoint = {
        id: newGuid(),
        position: { lat: parseFloat(String(latitude)), lng: parseFloat(String(longitude)) },
        altitude: parseFloat(String(altitude)),
        meta,
      };

      if (insertIndex !== null) {
        const updatedPoints = [...points];
        updatedPoints.splice(insertIndex, 0, newPoint);
        setPoints(updatedPoints);
      } else {
        setPoints(prevPoints => [...prevPoints, newPoint]);
      }

      setAltitude(300);
      setLatitude(parseFloat(currentPosition.lat.toFixed(COORDINATE_PRECISION)));
      setLongitude(parseFloat(currentPosition.lng.toFixed(COORDINATE_PRECISION)));
      setMeta([purposes[0].value, purposes[1].value]);
      setPendingPosition(null);
      setInsertIndex(null);
    }
    setOpenDialog(false);
  };

  const updatePosition = (newPosition: LatLngPosition) => {
    setCurrentPosition(newPosition);
    setMapCenter(newPosition);
    setLatitude(parseFloat(newPosition.lat.toFixed(COORDINATE_PRECISION)));
    setLongitude(parseFloat(newPosition.lng.toFixed(COORDINATE_PRECISION)));
  };

  const updateMapCenter = (newCenter: LatLngPosition) => {
    setMapCenter(newCenter);
  };

  const handlePolylineClick = (e: L.LeafletMouseEvent) => {
    const clickLatLng = e.latlng;

    if (!clickLatLng || typeof clickLatLng.lat !== 'number' || typeof clickLatLng.lng !== 'number') {
      console.error('Invalid clickLatLng:', clickLatLng);
      return;
    }

    let closestIndex = -1;
    let minDistance = Infinity;

    points.forEach((point, index) => {
      if (index < points.length - 1) {
        const segmentStart = points[index]?.position;
        const segmentEnd = points[index + 1]?.position;

        if (segmentStart && segmentEnd &&
          typeof segmentStart.lat === 'number' && typeof segmentStart.lng === 'number' &&
          typeof segmentEnd.lat === 'number' && typeof segmentEnd.lng === 'number') {

          const distance = L.LineUtil.pointToSegmentDistance(
            L.point(clickLatLng.lng, clickLatLng.lat),
            L.point(segmentStart.lng, segmentStart.lat),
            L.point(segmentEnd.lng, segmentEnd.lat)
          );

          if (distance < minDistance) {
            minDistance = distance;
            closestIndex = index;
          }
        }
      }
    });

    if (closestIndex !== -1) {
      const alt = (points[closestIndex].altitude + points[closestIndex + 1].altitude) / 2;

      const newPoint: FlightPoint = {
        id: newGuid(),
        position: {
          lat: parseFloat(clickLatLng.lat.toFixed(COORDINATE_PRECISION)),
          lng: parseFloat(clickLatLng.lng.toFixed(COORDINATE_PRECISION)),
        },
        altitude: parseFloat(String(alt)),
        meta: [purposes[0].value, purposes[1].value],
      };
      const pointInfo: CalculatedPointInfo = {
        bat: 100,
        time: 0,
      };

      const updatedPoints = [...points];
      updatedPoints.splice(closestIndex + 1, 0, newPoint);
      const updatedInfo = [...calculatedPointInfo];
      updatedInfo.splice(closestIndex + 1, 0, pointInfo);

      setPoints(updatedPoints);
      setCalculatedPointInfo(updatedInfo);
    }
  };

  interface ProcessedRectangle {
    northWest: { lat: number; lon: number };
    southEast: { lat: number; lon: number };
    fence_type: string;
  }

  const processRectangle = (rectangle: MapRectangle): ProcessedRectangle | null => {
    if (!rectangle || !rectangle.bounds || !rectangle.color) {
      console.error('Invalid rectangle:', rectangle);
      return null;
    }

    const bounds = rectangle.bounds as L.LatLngBounds;
    const southWest = bounds.getSouthWest?.() ?? (bounds as unknown as { _southWest: L.LatLng })._southWest;
    const northEast = bounds.getNorthEast?.() ?? (bounds as unknown as { _northEast: L.LatLng })._northEast;

    const northWest = { lat: northEast.lat, lon: southWest.lng };
    const southEast = { lat: southWest.lat, lon: northEast.lng };

    const fenceType = rectangle.color === 'red' ? 'EXCLUSION' : rectangle.color === 'green' ? 'INCLUSION' : 'UNKNOWN';
    return {
      northWest,
      southEast,
      fence_type: fenceType,
    };
  };

  const isRectangleDuplicate = (existingRectangles: ProcessedRectangle[], newRectangle: ProcessedRectangle) => {
    const newPointsString = JSON.stringify({ nw: newRectangle.northWest, se: newRectangle.southEast });
    return existingRectangles.some(rectangle => {
      const existingPointsString = JSON.stringify({ nw: rectangle.northWest, se: rectangle.southEast });
      return existingPointsString === newPointsString;
    });
  };

  const exportMap = () => {
    const processedRectangles = rectangles.map(processRectangle).filter((rectangle): rectangle is ProcessedRectangle => rectangle !== null);

    const uniqueRectangles: ProcessedRectangle[] = [];
    processedRectangles.forEach(rectangle => {
      if (!isRectangleDuplicate(uniqueRectangles, rectangle)) {
        uniqueRectangles.push(rectangle);
      }
    });

    const data = {
      geofences: {
        polygons: uniqueRectangles.map(rect => ({
          northWest: rect.northWest,
          southEast: rect.southEast,
        })),
      },
      action_points: points.map(point => ({
        lat: point.position.lat,
        lon: point.position.lng,
      })),
    };

    setJsonText(JSON.stringify(data, null, 2));
    setJsonDialogOpen(true);
  };

  const handleJsonDialogClose = () => {
    setJsonDialogOpen(false);
  };

  const handleJsonSave = (updatedJson: string) => {
    try {
      const data = JSON.parse(updatedJson);

      if (data.action_points && Array.isArray(data.action_points)) {
        const importedPoints: FlightPoint[] = data.action_points.map((ap: Record<string, unknown>) => ({
          id: newGuid(),
          position: {
            lat: (ap.point as Record<string, number>)?.lat || (ap.lat as number),
            lng: (ap.point as Record<string, number>)?.lon || (ap.lon as number),
          },
          altitude: parseFloat(String(ap.height || 300)),
          meta: (ap.action_specific as Record<string, string[]>)?.targets || [purposes[0].value, purposes[1].value],
        }));
        setPoints(importedPoints);
      }

      if (data.geofences?.polygons && Array.isArray(data.geofences.polygons)) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const importedRectangles: MapRectangle[] = data.geofences.polygons.map((polygon: any) => {
          const bounds = L.latLngBounds(
            [polygon.southEast.lat, polygon.northWest.lon],
            [polygon.northWest.lat, polygon.southEast.lon]
          );
          const color = polygon.fence_type === 'EXCLUSION' ? 'red' : 'green';
          return {
            id: newGuid(),
            bounds,
            color,
          };
        });
        setRectangles(importedRectangles);
      }

      if (data.operational_height?.currentAltitude) {
        setCurrentAltitude(data.operational_height.currentAltitude);
      }

      setJsonDialogOpen(false);
    } catch {
      alert('Invalid JSON format');
    }
  };

  const editAsJson = () => {
    const processedRectangles = rectangles.map(processRectangle).filter((rectangle): rectangle is ProcessedRectangle => rectangle !== null);

    const uniqueRectangles: ProcessedRectangle[] = [];
    processedRectangles.forEach(rectangle => {
      if (!isRectangleDuplicate(uniqueRectangles, rectangle)) {
        uniqueRectangles.push(rectangle);
      }
    });

    const data = {
      operational_height: { currentAltitude },
      geofences: {
        polygons: uniqueRectangles,
      },
      action_points: points.map(point => ({
        point: { lat: point.position.lat, lon: point.position.lng },
        height: Number(point.altitude),
        action: 'search',
        action_specific: {
          targets: point.meta,
        },
      })),
    };

    setJsonText(JSON.stringify(data, null, 2));
    setJsonDialogOpen(true);
  };

  return (
    <>
      <div className={'flight-plan-board'}>
        <LeftBoard
          points={points}
          setPoints={setPoints}
          calculatedPointInfo={calculatedPointInfo}
          setCalculatedPointInfo={setCalculatedPointInfo}
          aircraft={aircraft}
          initialAltitude={initialAltitude}
          setInitialAltitude={setInitialAltitude}
          currentAltitude={currentAltitude}
          actionMode={actionMode}
          setActionMode={setActionMode}
          setRectangleColor={setRectangleColor}
          editAsJson={editAsJson}
          exportMap={exportMap}
          updatePosition={updatePosition}
          currentPosition={mapCenter}
        />

        <MapView
          points={points}
          setPoints={setPoints}
          calculatedPointInfo={calculatedPointInfo}
          setCalculatedPointInfo={setCalculatedPointInfo}
          initialAltitude={initialAltitude}
          currentPosition={currentPosition}
          updatePosition={updatePosition}
          updateMapCenter={updateMapCenter}
          handlePolylineClick={handlePolylineClick}
          rectangles={rectangles}
          setRectangles={setRectangles}
          rectangleColor={rectangleColor}
          actionMode={actionMode}
        />

        <AltitudeDialog
          openDialog={openDialog}
          handleDialogClose={handleDialogClose}
          handleAltitudeSubmit={handleAltitudeSubmit}
          altitude={altitude}
          setAltitude={setAltitude}
          latitude={latitude}
          setLatitude={setLatitude}
          longitude={longitude}
          setLongitude={setLongitude}
          meta={meta}
          setMeta={setMeta}
        />

        <JsonEditorDialog
          open={jsonDialogOpen}
          jsonText={jsonText}
          onClose={handleJsonDialogClose}
          onSave={handleJsonSave}
        />
      </div>
      <div className='mobile-message'>
        <RotatePhoneIcon width='100px' height='100px' color='#000000' />
        Please switch to the landscape mode
      </div>
    </>
  );
}
