import { useEffect, useRef, useState } from 'react'
import GeoJSON from 'ol/format/GeoJSON'
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
import { OSM, Vector as VectorSource, XYZ } from 'ol/source'
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
import { Divider, IconButton, Slider, Stack, Select as MUISelect, MenuItem, Box } from '@mui/material'
import { Adjust, Api, CircleOutlined, OpenWith, RectangleOutlined, Rule, Straighten, Timeline, Undo, Warning } from '@mui/icons-material'
import { Type } from 'ol/geom/Geometry'
import { click, noModifierKeys, shiftKeyOnly } from 'ol/events/condition'
import Feature from 'ol/Feature'
import { SatelliteMapsProvider } from '../../interfaces/map'
import { containsExtent } from 'ol/extent'
import { drawingLayerStyle, regionsLayerStyle, selectStyle } from './MapStyles'
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
import { mapCenter, mapExtent } from './MapConstants'

const MapComponent = () => {
    const mapElement = useRef<HTMLDivElement | null>(null)
    const [currentTool, setCurrentTool] = useState<Type | null>(null)

    const map = useRef<Map | null>(null)

    const [satMapsProvider, setSatMapsProvider] = useState<SatelliteMapsProvider>('yandex')

    const gMapsSatSource = useRef<XYZ>(googleMapsSatelliteSource)

    const yMapsSatSource = useRef<XYZ>(yandexMapsSatelliteSource)

    const satLayer = useRef<TileLayer>(new TileLayer({
        source: gMapsSatSource.current,
    }))

    const draw = useRef<Draw | null>(null)
    const snap = useRef<Snap | null>(null)

    const selectFeature = useRef<Select>(new Select({
        condition: function (mapBrowserEvent) {
            return click(mapBrowserEvent) && shiftKeyOnly(mapBrowserEvent);
        },
    }))

    const drawingLayer = useRef<VectorLayer | null>(null)
    const drawingLayerSource = useRef<VectorSource>(new VectorSource())

    const regionsLayer = useRef<VectorLayer>(new VectorLayer({
        source: regionsLayerSource,
        style: regionsLayerStyle
    }))

    const selectedRegion = useRef<Feature | null>(null)

    const baseLayer = useRef<TileLayer>(new TileLayer({
        source: new OSM(),
    }))

    const addInteractions = () => {
        if (currentTool) {
            draw.current = new Draw({
                source: drawingLayerSource.current,
                type: currentTool,
                condition: noModifierKeys
            })
            map?.current?.addInteraction(draw.current)
            snap.current = new Snap({ source: drawingLayerSource.current })
            map?.current?.addInteraction(snap.current)
        }
    }

    // Function to save features to localStorage
    const saveFeatures = () => {
        const features = drawingLayer.current?.getSource()?.getFeatures()
        if (features && features.length > 0) {
            const geoJSON = new GeoJSON()
            const featuresJSON = geoJSON.writeFeatures(features)
            localStorage.setItem('savedFeatures', featuresJSON)
        }

        console.log(drawingLayer.current?.getSource()?.getFeatures())
    }

    // Function to load features from localStorage
    const loadFeatures = () => {
        const savedFeatures = localStorage.getItem('savedFeatures')
        if (savedFeatures) {
            const geoJSON = new GeoJSON()
            const features = geoJSON.readFeatures(savedFeatures, {
                featureProjection: 'EPSG:4326', // Ensure the projection is correct
            })
            drawingLayerSource.current?.addFeatures(features)  // Add features to the vector source
            //drawingLayer.current?.getSource()?.changed()
        }
    }

    const handleToolSelect = (tool: Type) => {
        if (currentTool == tool) {
            setCurrentTool(null)
        } else {
            setCurrentTool(tool)
        }
    }

    const zoomToFeature = (feature: Feature) => {
        const geometry = feature.getGeometry()
        const extent = geometry?.getExtent()

        if (map.current && extent) {
            map.current.getView().fit(extent, {
                duration: 300,
                maxZoom: 19,
            })
        }
    }

    useEffect(() => {
        drawingLayer.current = new VectorLayer({
            source: drawingLayerSource.current,
            style: drawingLayerStyle,
        })

        map.current = new Map({
            layers: [baseLayer.current, satLayer.current, regionsLayer.current, drawingLayer.current],
            target: mapElement.current as HTMLDivElement,
            view: new View({
                center: mapCenter,
                zoom: 2,
                maxZoom: 21,
                extent: mapExtent,
            }),
        })

        const modify = new Modify({ source: drawingLayerSource.current })
        map.current.addInteraction(modify)

        map.current.addInteraction(selectFeature.current)

        selectFeature.current.on('select', (e) => {
            const selectedFeatures = e.selected

            if (selectedFeatures.length > 0) {
                selectedFeatures.forEach((feature) => {
                    drawingLayerSource.current?.removeFeature(feature)
                })
            }
        })

        loadFeatures()

        // Show current selected region
        map.current.on('pointermove', function (e) {
            if (selectedRegion.current !== null) {
                selectedRegion.current.setStyle(undefined)
                selectedRegion.current = null
            }

            if (map.current) {
                map.current.forEachFeatureAtPixel(e.pixel, function (f) {
                    selectedRegion.current = f as Feature
                    selectedRegion.current.setStyle(selectStyle)

                    if (f.get('district')) {
                        setStatusText(f.get('district'))
                    }

                    return true
                })
            }
        })

        map.current.on('click', function (e) {
            if (selectedRegion.current !== null) {
                selectedRegion.current = null
            }

            if (map.current) {
                map.current.forEachFeatureAtPixel(e.pixel, function (f) {
                    selectedRegion.current = f as Feature
                    // Zoom to the selected feature
                    zoomToFeature(selectedRegion.current)

                    return true
                });
            }

        })

        // Hide regions layer when fully visible
        map.current.on('moveend', function () {
            const viewExtent = map.current?.getView().calculateExtent(map.current.getSize())
            const features = regionsLayer.current.getSource()?.getFeatures()

            let isViewCovered = false

            features?.forEach((feature: Feature) => {
                const featureExtent = feature?.getGeometry()?.getExtent()
                if (viewExtent && featureExtent) {
                    if (containsExtent(featureExtent, viewExtent)) {
                        isViewCovered = true
                    }
                }
            })

            regionsLayer.current.setVisible(!isViewCovered)
        })

        return () => {
            map?.current?.setTarget(undefined)
        }
    }, [])

    useEffect(() => {
        if (currentTool) {
            if (draw.current) map?.current?.removeInteraction(draw.current)
            if (snap.current) map?.current?.removeInteraction(snap.current)
            addInteractions()
        } else {
            if (draw.current) map?.current?.removeInteraction(draw.current)
            if (snap.current) map?.current?.removeInteraction(snap.current)
        }
    }, [currentTool])

    const [satelliteOpacity, setSatelliteOpacity] = useState<number>(0)

    const [statusText, setStatusText] = useState('')

    // Visibility setting
    useEffect(() => {
        satLayer.current?.setOpacity(satelliteOpacity)

        if (satelliteOpacity == 0) {
            baseLayer.current?.setVisible(true)
            satLayer.current?.setVisible(false)
        } if (satelliteOpacity == 1) {
            baseLayer.current?.setVisible(false)
            satLayer.current?.setVisible(true)
        } else if (satelliteOpacity > 0 && satelliteOpacity < 1) {
            baseLayer.current?.setVisible(true)
            satLayer.current?.setVisible(true)
        }
    }, [satelliteOpacity])

    // Satellite tiles setting
    useEffect(() => {
        satLayer.current?.setSource(satMapsProvider == 'google' ? gMapsSatSource.current : satMapsProvider == 'yandex' ? yMapsSatSource.current : gMapsSatSource.current)
        satLayer.current?.getSource()?.refresh()
    }, [satMapsProvider])

    return (
        <Stack flex={1} flexDirection='column'>
            <Stack my={1} spacing={1} direction='row' divider={<Divider orientation='vertical' flexItem />}>

                <Stack flex={1} alignItems='center' justifyContent='center'>
                    <Slider aria-label="Opacity" min={0} max={1} step={0.001} defaultValue={satelliteOpacity} value={satelliteOpacity} onChange={(_, value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />
                </Stack>

                <MUISelect
                    variant='standard'
                    labelId="demo-simple-select-label"
                    id="demo-simple-select"
                    value={satMapsProvider}
                    label="Satellite Provider"
                    onChange={(e) => setSatMapsProvider(e.target.value as SatelliteMapsProvider)}
                >
                    <MenuItem value={'google'}>Google</MenuItem>
                    <MenuItem value={'yandex'}>Яндекс</MenuItem>
                </MUISelect>


                <IconButton onClick={() => {
                    fetch(`${import.meta.env.VITE_API_EMS_URL}/hello`, { method: 'GET' }).then(res => console.log(res))
                }}>
                    <Api />
                </IconButton>

                <IconButton onClick={() => {
                    saveFeatures()
                }}>
                    <Warning />
                </IconButton>

                <IconButton
                    onClick={() => {
                        draw.current?.removeLastPoint()
                    }}>
                    <Undo />
                </IconButton>

                <IconButton
                    sx={{ backgroundColor: currentTool === 'Point' ? 'Highlight' : 'transparent' }}
                    onClick={() => handleToolSelect('Point')}>
                    <Adjust />
                </IconButton>

                <IconButton
                    sx={{ backgroundColor: currentTool === 'LineString' ? 'Highlight' : 'transparent' }}
                    onClick={() => handleToolSelect('LineString')}>
                    <Timeline />
                </IconButton>

                <IconButton
                    sx={{ backgroundColor: currentTool === 'Polygon' ? 'Highlight' : 'transparent' }}
                    onClick={() => handleToolSelect('Polygon')}>
                    <RectangleOutlined />
                </IconButton>

                <IconButton
                    sx={{ backgroundColor: currentTool === 'Circle' ? 'Highlight' : 'transparent' }}
                    onClick={() => handleToolSelect('Circle')}>
                    <CircleOutlined />
                </IconButton>

                <IconButton
                    onClick={() => map?.current?.addInteraction(new Translate())}
                >
                    <OpenWith />
                </IconButton>

                <IconButton>
                    <Straighten />
                </IconButton>
            </Stack>

            <Box>
                <div id="map-container" ref={mapElement} style={{ width: '100%', height: '600px', maxHeight: '100%', position: 'relative', flexGrow: 1 }}></div>
            </Box>

            <Stack>
                {statusText}
            </Stack>
        </Stack>
    );
};

export default MapComponent
