import { useEffect, useRef } from 'react'
import 'ol/ol.css'
import { Container, Stack, Tabs } from '@mantine/core'
import OlMap from 'ol/Map'
import { v4 as uuidv4 } from 'uuid'
import TileLayer from 'ol/layer/Tile'
import { OSM } from 'ol/source'
import View from 'ol/View'
import { transform } from 'ol/proj'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import Feature from 'ol/Feature'
import { LineString } from 'ol/geom'
import { Stroke, Style, Text } from 'ol/style'

const center = [14443331.466543002, 8878970.176309839]

const style = new Style({
    stroke: new Stroke({ color: 'blue', width: 2 }),
    text: new Text({
        font: 'bold 14px',
        placement: 'line',
        offsetY: -14
    }),
})

const MapLineTest = () => {
    const lines = [
        { name: 'A', points: [[100, 100], [200, 200]] },
        { name: 'B', points: [[200, 200], [300, 200]] },
        { name: 'X', points: [[200, 200], [300, 300]] },
        { name: 'L', points: [[300, 300], [350, 300]] },
        { name: 'N', points: [[300, 300], [250, 300]] },
        { name: 'I', points: [[300, 200], [300, 100]] },
        { name: 'J', points: [[300, 100], [250, 50]] },
        { name: 'C', points: [[300, 200], [400, 150]] },
        { name: 'D', points: [[400, 150], [400, 100]] },
    ]

    const map = useRef<OlMap | null>(null)
    const vectorLayer = useRef(new VectorLayer({
        source: new VectorSource(),
        style: feature => {
            style.getText()?.setText(feature.get('label'))
            return style
        }
    }))

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

    useEffect(() => {
        map.current = new OlMap({
            controls: [],
            layers: [
                new TileLayer({ source: new OSM(), properties: { id: uuidv4(), name: 'OpenStreetMap' } }),
                vectorLayer.current
            ]
        })
        map.current.setTarget(mapElement.current as HTMLDivElement)
        map.current.setView(new View({
            center: transform([129.7466541, 62.083504], 'EPSG:4326', 'EPSG:3857'),
            zoom: 16,
            maxZoom: 21,
        }))
    }, [])

    useEffect(() => {
        if (map.current) {
            const graph = new Map<string, { node: string, distance: number }[]>()

            // build graph adjacency list
            lines.forEach(({ points }) => {
                const [start, end] = points.map(pt => pt.join(','))
                const distance = Math.hypot(points[1][0] - points[0][0], points[1][1] - points[0][1])

                if (!graph.has(start)) graph.set(start, [])
                if (!graph.has(end)) graph.set(end, [])

                graph.get(start)?.push({ node: end, distance })
                graph.get(end)?.push({ node: start, distance })
            })

            // perform DFS to calculate distances from "A"
            const startNode = lines[0].points[0].join(',')
            const distances = new Map<string, number>()
            const dfs = (node: string, currentDist: number) => {
                if (distances.has(node) && distances.get(node)! <= currentDist) return
                distances.set(node, currentDist)
                for (const { node: neighbor, distance } of graph.get(node) || []) {
                    dfs(neighbor, currentDist + distance)
                }
            }
            dfs(startNode, 0)

            // render features
            lines.forEach(({ name, points }) => {
                const lineCoords = points.map(point => [point[0] + center[0], point[1] + center[1]])
                const feature = new Feature(new LineString(lineCoords))

                const lastNode = points[1].join(',')
                const label = `${name} ${feature.getGeometry()?.getLength().toFixed(2)} (${distances.get(lastNode)?.toFixed(1) ?? '∞'})`

                feature.set('label', label)
                vectorLayer.current.getSource()?.addFeature(feature)
            })
        }
    }, [])

    return (
        <Container fluid w='100%' pos='relative' p={0}>
            <Tabs h='100%' variant='default' value={'map'} keepMounted={true}>
                <Stack gap={0} h='100%'>
                    <Tabs.List>
                        <Tabs.Tab value={'map'}>Map</Tabs.Tab>
                    </Tabs.List>
                    <Tabs.Panel value={'map'} h='100%' pos='relative'>
                        <Container pos='absolute' fluid p={0} w='100%' h='100%' ref={mapElement}></Container>
                    </Tabs.Panel>
                </Stack>
            </Tabs>
        </Container>
    )
}

export default MapLineTest
