import React, { useCallback, useEffect, useRef, useState } from 'react';
import {
  View,
  StyleSheet,
  TouchableOpacity,
  Text,
  ScrollView,
  Platform,
  Linking,
  ActivityIndicator as RNActivityIndicator
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Modal, FlatList as List, Header, WarningModal } from 'src/components';
import * as turf from '@turf/turf';
import * as MapLibreRN from '@maplibre/maplibre-react-native';
import NetInfo from '@react-native-community/netinfo';
import * as Location from 'expo-location';

import { getFontSize } from 'src/utils';
import { useGetRegionsForTripsQuery } from '@api/trips';
import { useGetListRegionsQuery } from '@api/regions';
import { Colors } from 'src/theme';

import SearchSvg from 'assets/icons/search.svg';
import SaveSvg from 'assets/icons/travels-screens/save.svg';
import { VECTOR_MAP_HOST } from 'src/constants';
import { storage, StoreType } from 'src/storage';
import { usePostGetMapDataForRegionMutation } from '@api/maps';
import { formatBytes } from '../formatters';
import { ActivityIndicator } from 'react-native-paper';
import { offlineMapManager } from '../OfflineMapManager';
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 OFFLINE_MAPS_KEY = 'offline_maps';

export const SelectRegionScreen = ({ navigation }: { navigation: any }) => {
  const token = storage.get('token', StoreType.STRING) as string;
  const { data } = useGetRegionsForTripsQuery(true);
  const { data: regionsList } = useGetListRegionsQuery(true);

  const [regions, setRegions] = useState<any[] | null>(null);
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [selectedRegions, setSelectedRegions] = useState<any[]>([]);
  const [regionsToSave, setRegionsToSave] = useState<any[]>([]);
  const [regionData, setRegionData] = useState<any | null>(null);
  const [isConnectedToWifi, setIsConnectedToWifi] = useState(true);

  const { mutateAsync: getMapDataForRegion } = usePostGetMapDataForRegionMutation();
  const [loadingSize, setLoadingSize] = useState(false);
  const [estimatedSize, setEstimatedSize] = useState(0);

  const [regionPopupVisible, setRegionPopupVisible] = useState(false);
  const mapRef = useRef<MapLibreRN.MapViewRef>(null);
  const cameraRef = useRef<MapLibreRN.CameraRef>(null);

  const [filterSelectedRegions, setFilterSelectedRegions] = useState<any[]>(generateFilter([]));
  const [modalState, setModalState] = useState({
    visible: false,
    type: 'confirm',
    action: () => {},
    message: '',
    title: ''
  });

  const pendingRegionRequests = new Set();
  const [isLoading, setIsLoading] = useState(false);
  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 [renderCamera, setRenderCamera] = useState(Platform.OS === 'ios');
  const isAnimatingRef = useRef(false);
  const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  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(() => {
    setIsLoading(pendingRegionRequests.size > 0);
  }, [loadingSize]);

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

  useEffect(() => {
    const checkConnectionType = async () => {
      try {
        const connectionInfo = await NetInfo.fetch();
        setIsConnectedToWifi(connectionInfo.type === 'wifi');
      } catch (error) {
        console.error('Error checking network connection:', error);
        setIsConnectedToWifi(true);
      }
    };

    checkConnectionType();
  }, []);

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

  const closeModal = () => {
    setModalState({
      visible: false,
      type: 'confirm',
      action: () => {},
      message: '',
      title: ''
    });
  };

  const fetchRegionData = async (regionId: number) => {
    if (pendingRegionRequests.has(regionId)) {
      return null;
    }

    pendingRegionRequests.add(regionId);
    setLoadingSize(true);

    try {
      const response = await getMapDataForRegion({ token, region_id: regionId });
      return response;
    } catch (error) {
      return null;
    } finally {
      pendingRegionRequests.delete(regionId);

      if (pendingRegionRequests.size === 0) {
        setLoadingSize(false);
      }
    }
  };

  const addRegionFromSearch = async (searchRegion: any) => {
    setLoadingSize(true);
    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);

      const regionData = await fetchRegionData(searchRegion.id);

      if (regionData && regionData.size) {
        setRegionsToSave((prevRegions) => [...prevRegions, { ...regionData, id: searchRegion.id }]);
        setEstimatedSize((prevSize) => prevSize + regionData.size);
      }

      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
          );
        }
      }
    } else {
      setLoadingSize(false);
    }
  };

  const handleSavePress = () => {
    if (!isConnectedToWifi) {
      setModalState({
        visible: true,
        type: 'success',
        action: () => startDownload(),
        message:
          "You're currently on a mobile connection. Downloading offline maps may use a large amount of data and could incur charges from your mobile provider. We recommend using Wi-Fi whenever possible.",
        title: 'Mobile Data Connection'
      });
    } else {
      startDownload();
    }
  };

  const startDownload = async () => {
    try {
      const results = {
        success: [],
        failed: []
      };

      for (const region of regionsToSave) {
        try {
          const [west, south, east, north] = region.bbox;

          const packId = `region_${region.id}_${Date.now()}`;

          const offlinePackOptions = {
            name: packId,
            styleURL: `${VECTOR_MAP_HOST}/nomadmania-maps2025.json`,
            bounds: [
              [west, south],
              [east, north]
            ],
            minZoom: 10,
            maxZoom: 14
          };

          const bounds = {
            north,
            south,
            east,
            west
          };

          const savedMapsString = storage.get(OFFLINE_MAPS_KEY, StoreType.STRING) as string;
          const savedMaps = savedMapsString ? JSON.parse(savedMapsString) : [];

          const mapData = {
            id: packId,
            regionId: region.id,
            name: region.name || `Region ${region.id}`,
            bounds: JSON.stringify(bounds),
            size: region.size || 0,
            updatedAt: Date.now(),
            status: 'downloading',
            progress: 0,
            styleURL: `${VECTOR_MAP_HOST}/nomadmania-maps2025.json`,
            minZoom: 10,
            maxZoom: 14
          };

          savedMaps.unshift(mapData);

          storage.set(OFFLINE_MAPS_KEY, JSON.stringify(savedMaps));

          offlineMapManager.createPack(
            offlinePackOptions,
            (progress: any) => {
              const mapsString = storage.get(OFFLINE_MAPS_KEY, StoreType.STRING) as string;
              const maps = mapsString ? JSON.parse(mapsString) : [];
              const mapIndex = maps.findIndex((map: any) => map.id === packId);

              if (mapIndex >= 0) {
                maps[mapIndex].progress = progress.percentage;

                if (progress.percentage === 100) {
                  maps[mapIndex].status = 'valid';
                  maps[mapIndex].size = progress.completedSize || maps[mapIndex].size;
                  maps[mapIndex].updatedAt = Date.now();
                }

                storage.set(OFFLINE_MAPS_KEY, JSON.stringify(maps));
              }
            },
            (error: any) => {
              console.error(`Download error for region ${region.id}:`, error);

              const mapsString = storage.get(OFFLINE_MAPS_KEY, StoreType.STRING) as string;
              const maps = mapsString ? JSON.parse(mapsString) : [];
              const mapIndex = maps.findIndex((map: any) => map.id === packId);

              if (mapIndex >= 0) {
                maps[mapIndex].status = 'invalid';
                maps[mapIndex].error = error.message || 'Unknown error';
                storage.set(OFFLINE_MAPS_KEY, JSON.stringify(maps));
              }
            }
          );
        } catch (error) {
          console.error(`Error processing region ${region.id}:`, error);
        }
      }

      navigation.goBack();
    } catch (error) {
      console.error('Error starting download:', error);
    }
  };

  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 || isLoading) return;
      setLoadingSize(true);

      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) {
              const regionToRemove = regionsToSave.find((region) => region.id == id);

              const newSelectedRegions = [...selectedRegions];
              newSelectedRegions.splice(regionIndex, 1);
              setSelectedRegions(newSelectedRegions);

              setEstimatedSize((prevSize) => {
                return regionToRemove ? prevSize - regionToRemove.size : prevSize;
              });

              setRegionsToSave(regionsToSave.filter((region) => region.id !== id));
              setRegionPopupVisible(false);
              setLoadingSize(false);
              return;
            } else {
              setSelectedRegions([...selectedRegions, selectedRegion.properties] as any);
              const regionData = await fetchRegionData(id);

              if (regionData && regionData.size) {
                setRegionsToSave((prevRegions) => [...prevRegions, { ...regionData, id }]);
                setEstimatedSize((prevSize) => prevSize + regionData.size);
                setLoadingSize(false);
              }
            }

            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);
        setLoadingSize(false);
      }
    },
    [selectedRegions, regions, regionsToSave]
  );

  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 ? (
            <RNActivityIndicator size="small" color={Colors.DARK_BLUE} />
          ) : (
            <LocationIcon />
          )}
        </TouchableOpacity>
      </View>

      <View style={styles.infoContainer}>
        {regionPopupVisible && regionData && (
          <View style={styles.popupWrapper}>
            <View style={styles.popupContainer}>
              <Text style={styles.popupText}>{regionData.name ?? regionData.region_name}</Text>
            </View>
          </View>
        )}
        <View style={styles.infoRow}>
          <Text style={styles.sizeText}>Estimated size: </Text>
          {loadingSize ? (
            <ActivityIndicator size={15} color={Colors.DARK_BLUE} />
          ) : (
            <Text style={[styles.sizeText, { fontWeight: '600' }]}>
              {formatBytes(estimatedSize)}
            </Text>
          )}
        </View>

        <Text style={[styles.sizeText, { marginBottom: 8 }]}>Regions:</Text>
        <ScrollView>
          {regionsToSave.map((region, index) => (
            <View style={{ marginBottom: 8 }} key={`${region.id}`}>
              <Text style={styles.tilesText}>{region.name}</Text>
            </View>
          ))}
        </ScrollView>
      </View>

      <Modal
        onRequestClose={() => setIsModalVisible(false)}
        headerTitle={'Select Regions'}
        visible={isModalVisible}
      >
        <List
          itemObject={(object) => {
            setIsModalVisible(false);
            setRegionData(object);
            addRegionFromSearch(object);
          }}
        />
      </Modal>

      <WarningModal
        type={modalState.type}
        isVisible={modalState.visible}
        message={modalState.message}
        title={modalState.title}
        action={modalState.action}
        onClose={() => closeModal()}
      />

      <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>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  wrapper: {
    marginLeft: '5%',
    marginRight: '5%',
    alignItems: 'center'
  },
  map: {
    ...StyleSheet.absoluteFillObject
  },
  cornerButton: {
    position: 'absolute',
    backgroundColor: Colors.WHITE,
    padding: 12,
    width: 48,
    height: 48,
    borderRadius: 24,
    alignItems: 'center',
    justifyContent: 'center',
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 1
    },
    shadowOpacity: 0.25,
    shadowRadius: 1.5,
    elevation: 2
  },
  bottomButton: {
    width: 42,
    height: 42,
    borderRadius: 21
  },
  bottomRightButton: {
    right: 16
  },
  searchContainer: {
    gap: 16,
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 8
  },
  regionSelector: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingHorizontal: 16,
    borderRadius: 4,
    height: 36,
    backgroundColor: Colors.FILL_LIGHT,
    justifyContent: 'flex-start',
    gap: 10,
    flex: 1
  },
  regionText: {
    fontSize: 15,
    fontWeight: '500',
    color: Colors.LIGHT_GRAY
  },
  saveBtn: {
    borderRadius: 20,
    paddingVertical: 10,
    paddingHorizontal: 16,
    borderWidth: 1
  },
  saveBtnActive: {
    borderColor: Colors.ORANGE,
    backgroundColor: Colors.ORANGE
  },
  saveBtnDisabled: {
    borderColor: Colors.LIGHT_GRAY
  },
  popupWrapper: {
    marginHorizontal: 24,
    position: 'absolute',
    top: -48,
    left: 0,
    right: 0
  },
  popupContainer: {
    alignItems: 'center',
    justifyContent: 'center',
    alignSelf: 'center',
    paddingVertical: 8,
    paddingHorizontal: 16,
    backgroundColor: 'rgba(33, 37, 41, 0.8)',
    borderRadius: 100
  },
  popupText: {
    color: Colors.WHITE,
    fontSize: getFontSize(12),
    fontWeight: '600'
  },
  infoContainer: {
    marginHorizontal: '5%',
    maxHeight: 150,
    position: 'relative'
  },
  infoRow: {
    flexDirection: 'row',
    gap: 4,
    marginBottom: 8,
    marginTop: 8
  },
  sizeText: {
    fontSize: 13,
    color: Colors.DARK_BLUE,
    fontFamily: 'montserrat-700'
  },
  tilesText: {
    fontSize: getFontSize(12),
    color: Colors.DARK_BLUE,
    fontWeight: '500'
  }
});
