import React, { useCallback, useEffect, useRef, useState } from 'react';
import { View, Text, TouchableOpacity, ActivityIndicator, Platform, Linking } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useNavigation } from '@react-navigation/native';
import * as turf from '@turf/turf';
import * as MapLibreRN from '@maplibre/maplibre-react-native';
import * as Location from 'expo-location';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';

import { Header, Modal, FlatList as List, WarningModal } from 'src/components';

import { VECTOR_MAP_HOST } from 'src/constants';
import { Colors } from 'src/theme';
import { NAVIGATION_PAGES } from 'src/types';
import { RegionAddData } from '../utils/types';
import { useGetRegionsForTripsQuery } from '@api/trips';
import { useGetListRegionsQuery } from '@api/regions';
import { styles } from './styles';

import SearchSvg from '../../../../../assets/icons/search.svg';
import SaveSvg from '../../../../../assets/icons/travels-screens/save.svg';
import LocationIcon from 'assets/icons/location.svg';

const generateFilter = (ids: number[]) => {
  return ids?.length ? ['any', ...ids.map((id) => ['==', 'id', id])] : ['==', 'id', -1];
};

let nm_regions = {
  id: 'regions',
  type: 'fill',
  source: 'regions',
  'source-layer': 'regions',
  style: {
    fillColor: 'rgba(15, 63, 79, 0)'
  },
  filter: ['all'],
  maxzoom: 16
};

let selected_region = {
  id: 'selected_region',
  type: 'fill',
  source: 'regions',
  'source-layer': 'regions',
  style: {
    fillColor: 'rgba(237, 147, 52, 0.7)'
  },
  maxzoom: 12
};

const AddRegionsScreen = ({ route }: { route: any }) => {
  const { regionsParams }: { regionsParams: RegionAddData[] } = route.params;
  const { data } = useGetRegionsForTripsQuery(true);
  const { data: regionsList } = useGetListRegionsQuery(true);
  const navigation = useNavigation();
  const tabBarHeight = useBottomTabBarHeight();

  const [regions, setRegions] = useState<RegionAddData[] | null>(null);
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [selectedRegions, setSelectedRegions] = useState<any[]>([]);
  const [regionsToSave, setRegionsToSave] = useState<RegionAddData[]>([]);
  const [regionData, setRegionData] = useState<RegionAddData | null>(null);
  const [regionPopupVisible, setRegionPopupVisible] = useState(false);
  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
  const cameraRef = useRef<MapLibreRN.CameraRef>(null);
  const [renderCamera, setRenderCamera] = useState(Platform.OS === 'ios');
  const isAnimatingRef = useRef(false);
  const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  const [filterSelectedRegions, setFilterSelectedRegions] = useState<any[]>(generateFilter([]));
  const [isLocationLoading, setIsLocationLoading] = useState(false);
  const [location, setLocation] = useState<any | null>(null);
  const [askLocationVisible, setAskLocationVisible] = useState<boolean>(false);
  const [openSettingsVisible, setOpenSettingsVisible] = useState<boolean>(false);

  const cameraController = {
    flyTo: useCallback((coordinates: number[], duration: number = 1000) => {
      isAnimatingRef.current = true;
      if (animationTimeoutRef.current) {
        clearTimeout(animationTimeoutRef.current);
      }

      if (Platform.OS === 'android') {
        setRenderCamera(true);
        requestAnimationFrame(() => {
          cameraRef.current?.flyTo(coordinates, duration);
        });
        animationTimeoutRef.current = setTimeout(() => {
          isAnimatingRef.current = false;
          setRenderCamera(false);
        }, duration + 200);
      } else {
        cameraRef.current?.flyTo(coordinates, duration);
        animationTimeoutRef.current = setTimeout(() => {
          isAnimatingRef.current = false;
        }, duration + 100);
      }
    }, []),

    setCamera: useCallback((config: any) => {
      isAnimatingRef.current = true;
      if (animationTimeoutRef.current) {
        clearTimeout(animationTimeoutRef.current);
      }

      if (Platform.OS === 'android') {
        setRenderCamera(true);
        requestAnimationFrame(() => {
          cameraRef.current?.setCamera(config);
        });
        animationTimeoutRef.current = setTimeout(
          () => {
            isAnimatingRef.current = false;
            setRenderCamera(false);
          },
          (config.animationDuration ?? 1000) + 200
        );
      } else {
        cameraRef.current?.setCamera(config);
        animationTimeoutRef.current = setTimeout(
          () => {
            isAnimatingRef.current = false;
          },
          (config.animationDuration ?? 1000) + 100
        );
      }
    }, []),

    fitBounds: useCallback((ne: number[], sw: number[], padding: number[], duration: number) => {
      isAnimatingRef.current = true;

      if (animationTimeoutRef.current) {
        clearTimeout(animationTimeoutRef.current);
      }

      if (Platform.OS === 'android') {
        setRenderCamera(true);

        requestAnimationFrame(() => {
          cameraRef.current?.fitBounds(ne, sw, padding, duration);
        });

        animationTimeoutRef.current = setTimeout(() => {
          isAnimatingRef.current = false;
          setRenderCamera(false);
        }, duration + 200);
      } else {
        cameraRef.current?.fitBounds(ne, sw, padding, duration);

        animationTimeoutRef.current = setTimeout(() => {
          isAnimatingRef.current = false;
        }, duration + 100);
      }
    }, [])
  };

  useEffect(() => {
    if (data && data.regions) {
      setRegions(data.regions);
    }
  }, [data]);

  useEffect(() => {
    const ids = selectedRegions.map((region) => region.id);
    setFilterSelectedRegions(generateFilter(ids));
  }, [selectedRegions]);

  useEffect(() => {
    const addRegionsAsync = async () => {
      if (regionsParams) {
        setRegionsToSave((prevRegions) => [...prevRegions, ...regionsParams]);

        setSelectedRegions(
          (prevSelectedRegions) => [...prevSelectedRegions, ...regionsParams] as any
        );
      }
    };

    addRegionsAsync();
  }, [regionsParams]);

  useEffect(() => {
    return () => {
      if (animationTimeoutRef.current) {
        clearTimeout(animationTimeoutRef.current);
      }
    };
  }, []);

  const addRegionFromSearch = async (searchRegion: RegionAddData) => {
    const regionIndex = selectedRegions.findIndex((region) => region.id === searchRegion.id);
    const regionFromApi = regions?.find((region) => region.id === searchRegion.id);

    if (regionIndex < 0 && regionFromApi) {
      const newRegion = {
        id: searchRegion.id,
        name: searchRegion.name
      };

      setSelectedRegions([...selectedRegions, newRegion] as any);
      setRegionsToSave((prevRegions) => [...prevRegions, regionFromApi]);
      setRegionPopupVisible(true);

      if (regionsList) {
        const region = regionsList.data.find((region) => region.id === searchRegion.id);
        if (region) {
          const bounds = turf.bbox(region.bbox);
          cameraController.fitBounds(
            [bounds[2], bounds[3]],
            [bounds[0], bounds[1]],
            [50, 50, 50, 50],
            600
          );
        }
      }
    }
  };

  const handleSavePress = () => {
    if (route.params?.isSharedTrip) {
      navigation.popTo(
        ...([
          NAVIGATION_PAGES.CREATE_SHARED_TRIP,
          { regionsToSave: regionsToSave, eventId: route.params?.editId }
        ] as never)
      );
    } else {
      navigation.popTo(
        ...([
          NAVIGATION_PAGES.ADD_TRIP,
          { regionsToSave: regionsToSave, editTripId: route.params?.editId }
        ] as never)
      );
    }
  };

  const handleSetRegionData = (regionId: number) => {
    const foundRegion = regions?.find((region) => region.id === regionId);

    if (foundRegion) {
      setRegionData(foundRegion);
      setRegionsToSave((prevRegions) => [...prevRegions, foundRegion]);
    }
  };

  const handleMapPress = useCallback(
    async (event: any) => {
      if (!mapRef.current) return;

      try {
        const { screenPointX, screenPointY } = event.properties;

        const { features } = await mapRef.current.queryRenderedFeaturesAtPoint(
          [screenPointX, screenPointY],
          undefined,
          ['regions']
        );

        if (features?.length) {
          const selectedRegion = features[0];

          if (selectedRegion.properties) {
            const id = selectedRegion.properties.id;

            const regionIndex = selectedRegions.findIndex((region) => region.id === id);

            if (regionIndex >= 0) {
              let newSelectedRegions = [...selectedRegions];
              newSelectedRegions = newSelectedRegions.filter((region) => region.id !== id);
              setSelectedRegions(newSelectedRegions);
              setRegionsToSave(regionsToSave.filter((region) => region.id !== id));
              setRegionPopupVisible(false);
              return;
            } else {
              setSelectedRegions([...selectedRegions, selectedRegion.properties] as any);
            }

            handleSetRegionData(id);
            setRegionPopupVisible(true);

            if (regionsList) {
              const region = regionsList.data.find((region) => region.id === id);
              if (region) {
                const bounds = turf.bbox(region.bbox);
                cameraController.fitBounds(
                  [bounds[2], bounds[3]],
                  [bounds[0], bounds[1]],
                  [50, 50, 50, 50],
                  600
                );
              }
            }
          }
        }
      } catch (error) {
        console.error('Failed to get coordinates on AddRegionsScreen', error);
      }
    },
    [selectedRegions, regions]
  );

  const handleGetLocation = async () => {
    setIsLocationLoading(true);
    try {
      let { status, canAskAgain } = await Location.getForegroundPermissionsAsync();
      const isServicesEnabled = await Location.hasServicesEnabledAsync();

      if (status === 'granted' && isServicesEnabled) {
        await getLocation();
      } else if (!canAskAgain || !isServicesEnabled) {
        setOpenSettingsVisible(true);
      } else {
        setAskLocationVisible(true);
      }
    } finally {
      setIsLocationLoading(false);
    }
  };

  const getLocation = async () => {
    try {
      let currentLocation = await Location.getCurrentPositionAsync({
        accuracy: Location.Accuracy.Balanced
      });
      setLocation(currentLocation.coords);

      if (currentLocation.coords) {
        cameraController.flyTo(
          [currentLocation.coords.longitude, currentLocation.coords.latitude],
          1000
        );
      }
    } catch (error) {
      console.error('Error fetching user location:', error);
    }
  };

  const handleAcceptPermission = async () => {
    setAskLocationVisible(false);
    let { status, canAskAgain } = await Location.requestForegroundPermissionsAsync();
    const isServicesEnabled = await Location.hasServicesEnabledAsync();

    if (status === 'granted' && isServicesEnabled) {
      getLocation();
    } else if (!canAskAgain || !isServicesEnabled) {
      setOpenSettingsVisible(true);
    }
  };

  return (
    <SafeAreaView style={{ height: '100%' }} edges={['top']}>
      <View style={styles.wrapper}>
        <Header label={'Add Regions'} />
        <View style={styles.searchContainer}>
          <TouchableOpacity style={[styles.regionSelector]} onPress={() => setIsModalVisible(true)}>
            <SearchSvg fill={Colors.LIGHT_GRAY} />
            <Text style={styles.regionText}>Search</Text>
          </TouchableOpacity>
          <TouchableOpacity
            style={[
              styles.saveBtn,
              selectedRegions.length ? styles.saveBtnActive : styles.saveBtnDisabled
            ]}
            onPress={handleSavePress}
            disabled={!selectedRegions.length}
          >
            <Text
              style={{
                fontSize: 12,
                fontWeight: '600',
                color: !selectedRegions.length ? Colors.LIGHT_GRAY : Colors.WHITE
              }}
            >
              Save
            </Text>
          </TouchableOpacity>
        </View>
      </View>

      <View style={styles.container}>
        <MapLibreRN.MapView
          ref={mapRef}
          style={styles.map}
          mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps2025.json'}
          rotateEnabled={false}
          attributionEnabled={false}
          onPress={handleMapPress}
        >
          {(Platform.OS === 'ios' || renderCamera) && <MapLibreRN.Camera ref={cameraRef} />}

          <MapLibreRN.LineLayer
            id="nm-regions-line-layer"
            sourceID={nm_regions.source}
            sourceLayerID={nm_regions['source-layer']}
            filter={nm_regions.filter as any}
            maxZoomLevel={nm_regions.maxzoom}
            style={{
              lineColor: 'rgba(14, 80, 109, 1)',
              lineWidth: ['interpolate', ['linear'], ['zoom'], 0, 0.2, 4, 1, 5, 1.5, 12, 3],
              lineWidthTransition: { duration: 300, delay: 0 }
            }}
            belowLayerID="waterway-name"
          />
          <MapLibreRN.FillLayer
            id={nm_regions.id}
            sourceID={nm_regions.source}
            sourceLayerID={nm_regions['source-layer']}
            filter={nm_regions.filter as any}
            style={nm_regions.style}
            maxZoomLevel={nm_regions.maxzoom}
            belowLayerID="nm-regions-line-layer"
          />

          {selectedRegions && selectedRegions.length > 0 ? (
            <MapLibreRN.FillLayer
              id={selected_region.id}
              sourceID={nm_regions.source}
              sourceLayerID={nm_regions['source-layer']}
              filter={filterSelectedRegions as any}
              style={selected_region.style}
              maxZoomLevel={selected_region.maxzoom}
              belowLayerID="nm-regions-line-layer"
            />
          ) : null}

          {location && (
            <MapLibreRN.UserLocation
              animated={true}
              showsUserHeadingIndicator={true}
              onPress={async () => {
                const currentZoom = await mapRef.current?.getZoom();
                const newZoom = (currentZoom || 0) + 2;

                cameraController.setCamera({
                  centerCoordinate: [location.longitude, location.latitude],
                  zoomLevel: newZoom,
                  animationDuration: 500,
                  animationMode: 'flyTo'
                });
              }}
            ></MapLibreRN.UserLocation>
          )}
        </MapLibreRN.MapView>

        <TouchableOpacity
          onPress={handleGetLocation}
          style={[
            styles.cornerButton,
            styles.bottomButton,
            styles.bottomRightButton,
            { bottom: 20 }
          ]}
        >
          {isLocationLoading ? (
            <ActivityIndicator size="small" color={Colors.DARK_BLUE} />
          ) : (
            <LocationIcon />
          )}
        </TouchableOpacity>
      </View>
      {regionPopupVisible && regionData && (
        <View style={styles.popupWrapper}>
          <View style={styles.popupContainer}>
            <Text style={styles.popupText}>{regionData.name ?? regionData.region_name}</Text>
          </View>
        </View>
      )}
      <Modal
        onRequestClose={() => setIsModalVisible(false)}
        headerTitle={'Select Regions'}
        visible={isModalVisible}
      >
        <List
          itemObject={(object) => {
            setIsModalVisible(false);
            setRegionData(object);
            addRegionFromSearch(object);
          }}
        />
      </Modal>

      <WarningModal
        type={'success'}
        isVisible={openSettingsVisible}
        onClose={() => setOpenSettingsVisible(false)}
        action={async () => {
          const isServicesEnabled = await Location.hasServicesEnabledAsync();

          if (!isServicesEnabled) {
            Platform.OS === 'ios'
              ? Linking.openURL('app-settings:')
              : Linking.sendIntent('android.settings.LOCATION_SOURCE_SETTINGS');
          } else {
            Platform.OS === 'ios' ? Linking.openURL('app-settings:') : Linking.openSettings();
          }
        }}
        message="NomadMania app needs location permissions to function properly. Open settings?"
      />

      <WarningModal
        type={'success'}
        isVisible={askLocationVisible}
        onClose={() => setAskLocationVisible(false)}
        action={handleAcceptPermission}
        message="To use this feature we need your permission to access your location. If you press OK your system will ask you to approve location sharing with NomadMania app."
      />
    </SafeAreaView>
  );
};

export default AddRegionsScreen;
