import { useEffect, useRef, useState } from 'react'
import 'ol/ol.css'
import { Modify } from 'ol/interaction'
import { ImageStatic, Vector as VectorSource } from 'ol/source'
import Feature from 'ol/Feature'
import { getCenter } from 'ol/extent'
import { highlightStyleRed, highlightStyleYellow } from './MapStyles'
import { customMapSource, googleMapsSatelliteSource, yandexMapsSatelliteSource } from './MapSources'
import { Geometry } from 'ol/geom'
import { fromExtent } from 'ol/geom/Polygon'
import { Coordinate } from 'ol/coordinate'
import { addFigures, addInteractions, addLines, getCitySettings, getFeatureByEntityId, handleImageDrop, loadFeatures, loadRegionBounds, loadRegionCapitals, zoomToFeature } from './mapUtils'
import useSWR, { SWRConfiguration } from 'swr'
import { fetcher } from '../../http/axiosInstance'
import { BASE_URL } from '../../constants'
import { IconBoxPadding, IconChevronLeft, } from '@tabler/icons-react'
import axios from 'axios'
import MapToolbar from './MapToolbar/MapToolbar'
import MapStatusbar from './MapStatusbar/MapStatusbar'
import { setAlignMode, setTypeRoles, useMapStore, setMapLabel, getChanges, } from '../../store/map'
import ObjectTree from '../Tree/ObjectTree'
import { setSelectedDistrict, setSelectedRegion, setSelectedYear, useObjectsStore } from '../../store/objects'
import ObjectParameters from './ObjectParameters/ObjectParameters'
import TabsPane, { ITabsPane } from './TabsPane/TabsPane'
//import { useSearchParams } from 'react-router-dom'
import GeoJSON from 'ol/format/GeoJSON'
import MapLegend from './MapLegend/MapLegend'
import MapMode from './MapMode'
import MapPrint from './MapPrint/MapPrint'
import { Button, Divider, Spinner, Portal, Tooltip, Drawer } from '@fluentui/react-components'
import { useAppStore } from '../../store/app'
import { setDistrictsData, setRegionsData } from '../../store/regions'
import View from 'ol/View'
import { Style } from 'ol/style'
import MapRegionSelect from './MapRegionSelect/MapRegionSelect'
import MapLayersSelect from './MapLayers/MapLayersSelect'
import MapObjectSearch from './MapObjectSearch/MapObjectSearch'
import { useCapitals } from '../../hooks/map/useCapitals'

const swrOptions: SWRConfiguration = {
    revalidateOnFocus: false
}

const MapComponent = ({
    id,
    active = false
}: {
    id: string,
    active: boolean,
}) => {
    const { colorScheme } = useAppStore()

    // Store
    const { selectedYear, currentObjectId, selectedRegion, selectedDistrict } = useObjectsStore().id[id]
    const {
        changes, currentChange,
        mode, map, currentTool, alignMode, satMapsProvider,
        selectedObjectType, measureDraw, draw, snap, translate,
        drawingLayerSource,
        satLayer, staticMapLayer, figuresLayer, linesLayer,
        regionsLayer, districtBoundLayer, districtsLayer,
        printAreaDraw, statusText, statusTextPosition, regionSelect, districtSelect,
        capitalsLayer
    } = useMapStore().id[id]

    // Tab settings
    const objectsPane: ITabsPane[] = [
        { title: 'Объекты', value: 'objects', view: <ObjectTree map_id={id} /> },
        { title: 'Неразмещенные', value: 'unplaced', view: <></> },
        { title: 'Другие', value: 'other', view: <></> },
    ]
    // const paramsPane: ITabsPane[] = [
    //     { title: 'История изменений', value: 'history', view: <></> },
    //     { title: 'Параметры', value: 'parameters', view: <ObjectParameters map_id={id} /> },
    //     { title: 'Вычисляемые', value: 'calculated', view: <></> }
    // ]

    // Map
    const mapElement = useRef<HTMLDivElement | null>(null)

    // Get type roles
    useSWR(`/gis/type-roles`, (url) => fetcher(url, BASE_URL.nest).then(res => {
        if (Array.isArray(res)) {
            setTypeRoles(id, res)
        }
        return res
    }), swrOptions)

    const { data: regionsData } = useSWR(`/general/regions`, (url) => fetcher(url, BASE_URL.nest).then(res => {
        setRegionsData(res)
        return res
    }), swrOptions)

    // Bounds: region
    const { data: regionBoundsData } = useSWR(`/gis/bounds/region`, (url) => fetcher(url, BASE_URL.nest), swrOptions)

    // Map init
    useEffect(() => {
        map?.setTarget(mapElement.current as HTMLDivElement)

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

        loadFeatures(id)
    }, [])

    // First step: On region bounds loaded
    useEffect(() => {
        if (regionsLayer && regionBoundsData) {
            loadRegionBounds(regionBoundsData, regionsLayer)
        }

        return () => {
            if (regionsLayer) {
                regionsLayer.getSource()?.clear()
            }
        }
    }, [regionBoundsData])

    useEffect(() => {
        if (regionsData && Array.isArray(regionsData) && capitalsLayer) {
            loadRegionCapitals(regionsData, capitalsLayer)
        }

        return () => {
            if (capitalsLayer) {
                capitalsLayer.getSource()?.clear()
            }
        }
    }, [regionsData, capitalsLayer])

    useEffect(() => {
        if (selectedRegion === null && regionBoundsData) {
            if (map) {
                const extent = regionsLayer.getSource()?.getExtent()

                if (extent) {
                    map?.setView(new View({
                        extent: extent,
                        showFullExtent: true,
                    }))

                    map.getView().fit(fromExtent(extent), { padding: [100, 100, 100, 100] })
                }

                regionsLayer.getSource()?.forEachFeature((feature) => {
                    if (feature.getProperties()['entity_id'] !== selectedRegion) {
                        feature.setStyle()
                    }
                })

                map.addInteraction(regionSelect)
            }
        }
    }, [regionBoundsData, selectedRegion, map, regionsLayer])

    useEffect(() => {
        if (selectedRegion && map) {
            regionsLayer.getSource()?.forEachFeature((feature) => {
                if (feature.getProperties()['entity_id'] !== selectedRegion) {
                    feature.setStyle(new Style())
                }
            })
        }
    }, [selectedRegion, map])

    // Last step: once selected scheme
    useEffect(() => {
        if (selectedDistrict && selectedYear && districtBoundLayer) {
            const bounds = new VectorSource({
                url: `${BASE_URL.nest}/gis/bounds/city/${selectedDistrict}`,
                format: new GeoJSON(),
            })

            districtBoundLayer.setSource(bounds)

            // 
            // bounds.on('featuresloadend', function () {
            //     map?.setView(new View({
            //         extent: bounds.getExtent()
            //     }))
            // })
        }

        return () => {
            if (districtBoundLayer) {
                districtBoundLayer.getSource()?.clear()
            }
        }
    }, [selectedDistrict, selectedYear, districtBoundLayer])

    // Edit Mode: add interaction on tool change
    useEffect(() => {
        if (currentTool) {
            if (draw) map?.removeInteraction(draw)
            //if (snap.current) map?.current?.removeInteraction(snap.current)
            addInteractions(id)
        } else {
            //map?.getInteractions().clear()
            //addInteractions(id)
            if (translate) map?.removeInteraction(translate)
            if (draw) map?.removeInteraction(draw)
            if (snap) map?.removeInteraction(snap)
            if (measureDraw) map?.removeInteraction(measureDraw)
        }
    }, [currentTool])

    // Satellite tiles setting
    useEffect(() => {
        if (satLayer) {
            if (satMapsProvider === 'google') {
                satLayer.setSource(googleMapsSatelliteSource)
            }

            if (satMapsProvider === 'yandex') {
                satLayer.setSource(yandexMapsSatelliteSource)
            }

            if (satMapsProvider === 'custom') {
                satLayer.setSource(customMapSource)
            }
        }
    }, [satMapsProvider, satLayer])

    useEffect(() => {
        if (!selectedObjectType || !map) return

        if (figuresLayer) {
            // Reset styles and apply highlight to matching features
            figuresLayer.getSource()?.getFeatures().forEach((feature) => {
                if (selectedObjectType == feature.get('type')) {
                    feature.setStyle(highlightStyleYellow)
                } else {
                    feature.setStyle(undefined) // Reset to default style
                }
            })
        }

        if (linesLayer) {
            // Reset styles and apply highlight to matching features
            linesLayer.getSource()?.getFeatures().forEach((feature) => {
                if (selectedObjectType == feature.get('type')) {
                    feature.setStyle(highlightStyleYellow)
                } else {
                    feature.setStyle(undefined) // Reset to default style
                }
            })
        }
    }, [selectedObjectType])

    useEffect(() => {
        if (currentObjectId) {
            if (figuresLayer) {
                // Reset styles and apply highlight to matching features
                figuresLayer.getSource()?.getFeatures().forEach((feature: Feature) => {
                    if (Array.isArray(feature.get('object_id')) ? feature.get('object_id')[0] === currentObjectId : currentObjectId === feature.get('object_id')) {
                        feature.setStyle(highlightStyleRed)

                        zoomToFeature(id, feature)
                    } else {
                        feature.setStyle(undefined) // Reset to default style
                    }
                })
            }

            if (linesLayer) {
                // Reset styles and apply highlight to matching features
                linesLayer.getSource()?.getFeatures().forEach((feature: Feature) => {
                    if (Array.isArray(feature.get('object_id')) ? feature.get('object_id')[0] === currentObjectId : currentObjectId === feature.get('object_id')) {
                        feature.setStyle(highlightStyleRed)

                        zoomToFeature(id, feature)
                    } else {
                        feature.setStyle(undefined) // Reset to default style
                    }
                })
            }
        }
    }, [currentObjectId, figuresLayer, linesLayer])

    const { data: districtsData } = useSWR(selectedRegion ? `/general/districts/?region_id=${selectedRegion}` : null, (url) => fetcher(url, BASE_URL.nest).then(res => {
        setDistrictsData(res)
        return res
    }), swrOptions)

    const { data: figuresData, isValidating: figuresValidating } = useSWR(
        selectedDistrict && selectedYear ? `/gis/figures/all?city_id=${selectedDistrict}&year=${selectedYear}&offset=0&limit=${10000}` : null,
        (url) => axios.get(url, {
            baseURL: BASE_URL.nest
        }).then((res) => res.data),
        swrOptions
    )

    const { data: linesData, isValidating: linesValidating } = useSWR(
        !figuresValidating && selectedDistrict && selectedYear ? `/gis/lines/all?city_id=${selectedDistrict}&year=${selectedYear}&offset=0&limit=${10000}` : null,
        (url) => axios.get(url, {
            baseURL: BASE_URL.nest
        }).then((res) => res.data),
        swrOptions
    )

    const { data: districtData } = useSWR(
        selectedDistrict ? `/gis/images/all?city_id=${selectedDistrict}` : null,
        (url) => axios.get(url, {
            baseURL: BASE_URL.nest
        }).then((res) => Array.isArray(res.data) ? res.data[0] : null),
        swrOptions
    )

    useEffect(() => {
        if (Array.isArray(districtsData)) {
            districtsData.map((district) => {
                if (district.id === selectedDistrict) {
                    setMapLabel(id, [district.name, district.district_name, selectedYear].join(' - '))
                }
            })
        }
    }, [districtsData, id, selectedDistrict, selectedYear])

    useEffect(() => {
        if (selectedDistrict === null) {
            setSelectedYear(id, null)

            map?.addInteraction(districtSelect)
        }

        if (selectedRegion === null) {
            setSelectedYear(id, null)
            setSelectedDistrict(id, null)
        }
    }, [selectedDistrict, selectedRegion, id, map])

    const [leftPaneHidden, setLeftPaneHidden] = useState(false)

    useEffect(() => {
        const districtBoundSource = districtBoundLayer.getSource()

        if (map && selectedDistrict && districtBoundSource && districtBoundSource.getFeatures().length > 0) {
            const center: Coordinate = getCenter(districtBoundSource.getExtent())
            const settings = getCitySettings()

            addFigures(center, figuresData, figuresLayer, settings, map)

            addLines(center, linesData, linesLayer, settings)
        }

    }, [map, figuresData, linesData, selectedDistrict, selectedYear, districtBoundLayer])

    useEffect(() => {
        if (selectedDistrict && districtData) {
            const settings = getCitySettings()

            const imageUrl = `${import.meta.env.VITE_API_EMS_URL}/tiles/static/${selectedDistrict}`
            const img = new Image()
            img.src = imageUrl
            img.onload = () => {
                if (map) {
                    const width = img.naturalWidth
                    const height = img.naturalHeight
                    //const k = (width < height ? width / height : height / width)
                    const k = settings.image_scale

                    const wk = width * k
                    const hk = height * k

                    const center = [settings.offset_x + (wk), settings.offset_y - (hk)]

                    const extent = [center[0] - (wk), center[1] - (hk), center[0] + (wk), center[1] + (hk)]

                    // Set up the initial image layer with the extent
                    const imageSource = new ImageStatic({
                        url: imageUrl,
                        imageExtent: extent,
                    })
                    staticMapLayer.setSource(imageSource)

                    //map.current.addLayer(imageLayer.current)
                }
            }
        }
    }, [selectedDistrict, districtData, staticMapLayer])

    useEffect(() => {
        if (map) {
            if (mode === 'print') {
                map.addInteraction(printAreaDraw)
            } else {
                map.removeInteraction(printAreaDraw)
            }
        }
    }, [mode, map, printAreaDraw])

    useEffect(() => {
        if (districtsData && Array.isArray(districtsData)) {
            const list: Number[] = []
            districtsData.map(district => {
                list.push(district.id as Number)
            })

            fetch(`${BASE_URL.nest}/gis/bounds/city`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ list })
            }).then(async response => {
                const data = await response.json()

                if (Array.isArray(data)) {
                    data.map(bound => {
                        const geoJson = new GeoJSON() //new GeoJSON({ featureProjection: 'EPSG:3857' })
                        const geometry = geoJson.readGeometry(bound) as Geometry

                        const feature = new Feature(geometry)
                        feature.setProperties(bound.properties)

                        districtsLayer.getSource()?.addFeature(feature)
                    })
                }
            })
        }
    }, [districtsData])

    const mapTooltipRef = useRef<HTMLDivElement>(null)

    useEffect(() => {
        if (mapTooltipRef.current) {
            const leftOffset = 30
            const topOffset = -30
            mapTooltipRef.current.style.left = (statusTextPosition[0] + leftOffset).toString() + 'px'
            mapTooltipRef.current.style.top = (statusTextPosition[1] + topOffset).toString() + 'px'
        }
    }, [statusTextPosition, mapTooltipRef])

    // zoom on region select
    useEffect(() => {
        if (selectedRegion && !selectedDistrict) {
            const feature = getFeatureByEntityId(selectedRegion, regionsLayer)

            if (feature) {
                regionSelect.getFeatures().push(feature)
                map?.setView(new View({
                    extent: feature?.getGeometry()?.getExtent(),
                    showFullExtent: true,
                }))
            }

            zoomToFeature(id, feature)
        } else if (selectedDistrict) {
            // zoom on district select
            const feature = getFeatureByEntityId(selectedDistrict, districtsLayer)

            if (feature) {
                districtSelect.getFeatures().push(feature)

                regionsLayer.setOpacity(0)
            }

            // map?.setView(new View({
            //     extent: feature?.getGeometry()?.getExtent(),
            //     showFullExtent: true,
            // }))

            zoomToFeature(id, feature)
        } else if (!selectedRegion) {
            setSelectedRegion(id, null)
            setSelectedYear(id, null)
        }
    }, [selectedRegion, selectedDistrict, id])

    useCapitals(capitalsLayer, !!selectedDistrict)

    useEffect(() => {
        if (selectedYear) {
            regionsLayer.setOpacity(0)
            districtsLayer.setOpacity(0)
        }
    }, [selectedYear])

    useEffect(() => {
        if (changes) {
            console.log(changes)
        }
    }, [changes])

    useEffect(() => {
        if (currentChange) {
            const figures = figuresLayer.getSource()?.getFeatures()
            const lines = linesLayer.getSource()?.getFeatures()

            const changes = getChanges(id).get(currentChange)

            if (figures && lines) {
                changes.map((f: { object_id: string, year: number, type: 'figure' | 'line', feature: Feature }) => {
                    const geometry = new GeoJSON()

                    const feature = geometry.readFeature(f.feature) as Feature<Geometry>

                    if (f.type === 'figure') {
                        figures.find(fig => fig.getProperties()['object_id'] === f.object_id && fig.getProperties()['year'] === f.year)?.setGeometry(feature.getGeometry())
                    } else if (f.type === 'line') {
                        lines.find(l => l.getProperties()['object_id'] === f.object_id && l.getProperties()['year'] === f.year)?.setGeometry(feature.getGeometry())
                    }
                })
            }
        }
    }, [currentChange])

    return (
        <div style={{ display: 'grid', gridTemplateRows: 'auto min-content', position: 'relative', width: '100%', height: '100%' }}>
            <MapPrint id={id} mapElement={mapElement} />

            {active &&
                <Portal mountNode={document.querySelector('#header-portal')}>
                    <div style={{ display: 'flex', gap: '1rem' }}>
                        <MapObjectSearch map_id={id} />

                        <Button icon={<IconBoxPadding />} appearance={alignMode ? 'primary' : 'transparent'} onClick={() => setAlignMode(id, !alignMode)} />

                        <MapLayersSelect map_id={id} />
                    </div>
                </Portal >
            }

            <div style={{ gridRow: '1 / span 1', display: 'grid', gridTemplateColumns: 'min-content auto', position: 'relative' }}>
                <div style={{ gridColumn: '1 / span 1', width: !selectedRegion || (!!selectedRegion && !selectedYear) ? '300px' : 'min-content', height: '100%', position: 'relative', display: 'flex', zIndex: '2' }}>
                    <MapRegionSelect map_id={id} />
                </div>

                <div ref={mapElement} id={id} key={id} style={{ gridColumn: '2 / span 1', position: 'relative', width: '100%', height: '100%', maxHeight: '100%', zIndex: '1', filter: colorScheme === 'dark' ? 'invert(100%) hue-rotate(180deg)' : 'unset' }} onDragOver={(e) => e.preventDefault()} onDrop={(e) => handleImageDrop(e, id)}>
                    <div>
                        {statusText && active && !selectedYear &&
                            <Tooltip hideDelay={0} showDelay={0} content={statusText} relationship='description' visible>
                                <div style={{ position: 'absolute', zIndex: 9999, userSelect: 'none', pointerEvents: 'none' }} ref={mapTooltipRef}>
                                    {/* {statusText} */}
                                </div>
                            </Tooltip>
                        }
                    </div>
                </div>

                <div style={{ position: 'absolute', display: 'flex', flexDirection: 'column', width: '100%', height: '100%' }}>
                    <div style={{ display: 'flex', height: '94%', padding: '0.5rem', flexGrow: 1 }}>
                        <div style={{ display: 'flex', flexDirection: 'column', width: '100%', maxWidth: '380px' }}>
                            <div style={{ display: 'flex', width: '100%', height: '100%', gap: '0.5rem' }}>
                                <Drawer style={{ borderRadius: '0.25rem', height: '100%', zIndex: 1 }} type='inline' open={!!selectedRegion && !!selectedDistrict && !!selectedYear && !leftPaneHidden}>
                                    <TabsPane defaultTab='objects' tabs={objectsPane} />
                                    <Divider />
                                    <ObjectParameters map_id={id} />
                                    {/* <TabsPane defaultTab='parameters' tabs={paramsPane} /> */}
                                </Drawer>

                                {!!selectedRegion && !!selectedDistrict && !!selectedYear &&
                                    <Button
                                        icon={<IconChevronLeft size={16}
                                            style={{
                                                transform: `${leftPaneHidden ? 'rotate(180deg)' : ''}`,
                                            }} />}

                                        style={{
                                            zIndex: '1',
                                            display: 'flex',
                                            height: 'min-content'
                                        }}
                                        appearance='subtle'
                                        onClick={() => setLeftPaneHidden(!leftPaneHidden)}
                                    />
                                }
                            </div>
                        </div>

                        <div style={{ display: 'flex', flexDirection: 'column', width: '100%', alignItems: 'center' }} >
                            <div style={{ display: 'flex', flexDirection: 'column', width: 'fit-content' }}>
                                {selectedDistrict && selectedYear && <MapMode map_id={id} />}
                            </div>
                        </div>

                        <div style={{ display: 'flex', flexDirection: 'column', width: '100%', maxWidth: '340px', alignItems: 'flex-end', justifyContent: 'space-between', gap: '1rem' }}>
                            <MapToolbar map_id={id} />

                            {!!selectedRegion && !!selectedDistrict && !!selectedYear &&
                                <MapLegend selectedDistrict={selectedDistrict} selectedYear={selectedYear} />
                            }
                        </div>
                    </div>
                </div>
            </div>

            {(linesValidating || figuresValidating) && (
                <div
                    style={{
                        position: "absolute",
                        inset: 0,
                        backgroundColor: "rgba(255, 255, 255, 0.6)",
                        display: "flex",
                        justifyContent: "center",
                        alignItems: "center",
                        zIndex: 9999,
                    }}
                >
                    <Spinner size="large" label="Загрузка..." />
                </div>
            )}

            <div style={{ gridRow: '2 / span 1', display: 'flex', bottom: '0', width: '100%' }}>
                <MapStatusbar
                    map_id={id}
                />
            </div>
        </div>
    )
}

export default MapComponent
