import Taro from '@tarojs/taro';
import { useEffect, useRef, useState } from 'react';
import { getDistance } from '../../../../utils/Robotaxi-public-method';
import { TripStatus } from '../../../constants/trip-status';
import {
    ANDROID_ROUTE_POINT_LIMIT,
    DEFAULT_ROUTE_POINT_LIMIT,
    Deps,
    PASSED_ROUTE_POLYLINE_ID,
    ROUTE_ERASE_BREAK_DISTANCE_FACTOR,
    ROUTE_ERASE_BREAK_DISTANCE_METERS,
    ROUTE_ERASE_MAX_DISTANCE_METERS,
    ROUTE_POLYLINE_BASE_ID,
    WALKING_POLYLINE_ID_OFFSET
} from './use-route-manager.shared';

type UsePolylineRenderParams = {
    deps: Deps;
};

const FIRST_ROUTE_ERASE_READY_DELAY_MS = 120;

// 统一获取路线降采样上限
const resolveRoutePointLimit = (isAndroid: boolean) => (
    isAndroid ? ANDROID_ROUTE_POINT_LIMIT : DEFAULT_ROUTE_POINT_LIMIT
);

// 轻量签名，避免轮询同一路线时重复 setState
const buildPointsSignature = (points: any[]) => {
    if (!Array.isArray(points) || points.length === 0) {
        return 'empty';
    }
    const firstPoint = points[0];
    const middlePoint = points[Math.floor(points.length / 2)];
    const lastPoint = points[points.length - 1];
    return [
        points.length,
        firstPoint?.latitude ?? '',
        firstPoint?.longitude ?? '',
        middlePoint?.latitude ?? '',
        middlePoint?.longitude ?? '',
        lastPoint?.latitude ?? '',
        lastPoint?.longitude ?? ''
    ].join('|');
};

export default function usePolylineRender(params: UsePolylineRenderParams) {
    const { deps } = params;
    const [polyline, setPolyline] = useState<any[]>([]);
    const [routePoints, setRoutePoints] = useState<any[]>([]);
    const [walkingPoints, setWalkingPoints] = useState<any[]>([]);
    const [walkingTime, setWalkingTime] = useState<number>(0);

    const polylineKeyRef = useRef<string>('');
    const routePointsSignatureRef = useRef<string>('empty');
    const lastRoutePointRef = useRef<{ latitude: number; longitude: number } | null>(null);
    const lastRouteUpdateAtRef = useRef<number>(0);
    const lastPolylineUpdateAtRef = useRef<number>(0);
    const currentFullRouteRef = useRef<any[]>([]);
    const lastErasedIndexRef = useRef<number>(0);
    const orderRouteInitializedRef = useRef<boolean>(false);
    const routeEraseReadyRef = useRef<boolean>(false);
    const pendingRouteEraseRef = useRef<any[] | null>(null);
    const routeEraseReadyTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

    const clearRouteEraseReadyTimer = () => {
        if (routeEraseReadyTimerRef.current) {
            clearTimeout(routeEraseReadyTimerRef.current);
            routeEraseReadyTimerRef.current = null;
        }
    };

    const applyRoutePoints = (points: any[]) => {
        const nextPoints = Array.isArray(points) ? points : [];
        const nextSignature = buildPointsSignature(nextPoints);
        if (routePointsSignatureRef.current === nextSignature) {
            return false;
        }
        routePointsSignatureRef.current = nextSignature;
        if (nextPoints.length > 0) {
            const firstPoint = nextPoints[0];
            const nextLatitude = Number(firstPoint?.latitude);
            const nextLongitude = Number(firstPoint?.longitude);
            if (Number.isFinite(nextLatitude) && Number.isFinite(nextLongitude)) {
                lastRoutePointRef.current = {
                    latitude: nextLatitude,
                    longitude: nextLongitude
                };
                lastRouteUpdateAtRef.current = Date.now();
            } else {
                lastRoutePointRef.current = null;
                lastRouteUpdateAtRef.current = 0;
            }
        } else {
            lastRoutePointRef.current = null;
            lastRouteUpdateAtRef.current = 0;
        }
        setRoutePoints(nextPoints);
        return true;
    };

    const reducePoints = (points: any[], maxCount: number) => {
        if (!Array.isArray(points) || points.length <= maxCount) {
            return points;
        }
        const step = Math.ceil(points.length / maxCount);
        const reducedPoints: any[] = [];
        for (let index = 0; index < points.length; index += step) {
            reducedPoints.push(points[index]);
        }
        const lastPoint = points[points.length - 1];
        if (reducedPoints[reducedPoints.length - 1] !== lastPoint) {
            reducedPoints.push(lastPoint);
        }
        return reducedPoints;
    };

    const resetRouteRuntimeState = () => {
        setRoutePoints([]);
        setWalkingPoints([]);
        setWalkingTime(0);
        setPolyline([]);
        polylineKeyRef.current = '';
        routePointsSignatureRef.current = 'empty';
        lastRoutePointRef.current = null;
        lastRouteUpdateAtRef.current = 0;
        lastPolylineUpdateAtRef.current = 0;
        currentFullRouteRef.current = [];
        lastErasedIndexRef.current = 0;
        orderRouteInitializedRef.current = false;
        routeEraseReadyRef.current = false;
        pendingRouteEraseRef.current = null;
        clearRouteEraseReadyTimer();
    };

    const applyErasedRoutePoints = (newPoints: any[]) => {
        currentFullRouteRef.current = [...newPoints];
        lastErasedIndexRef.current = 0;
        applyRoutePoints([...newPoints]);
    };

    const erasePassedRouteSegments = (newPoints: any[], _servedPoints: any[] = []) => {
        const oldPoints = currentFullRouteRef.current;
        if (!oldPoints?.length || !newPoints?.length) {
            currentFullRouteRef.current = [...newPoints];
            applyRoutePoints([...newPoints]);
            return;
        }
        const boundaryPoint = newPoints[0];
        if (
            !boundaryPoint
            || typeof boundaryPoint.latitude !== 'number'
            || typeof boundaryPoint.longitude !== 'number'
        ) {
            return;
        }

        let bestMatchIndex = -1;
        let bestDistance = Infinity;
        const startIndex = lastErasedIndexRef.current;
        for (let index = startIndex; index < oldPoints.length; index += 1) {
            const distance = getDistance(
                oldPoints[index].latitude,
                oldPoints[index].longitude,
                boundaryPoint.latitude,
                boundaryPoint.longitude
            );
            if (distance < bestDistance) {
                bestDistance = distance;
                bestMatchIndex = index;
            }
            if (
                distance > bestDistance * ROUTE_ERASE_BREAK_DISTANCE_FACTOR
                && bestDistance < ROUTE_ERASE_BREAK_DISTANCE_METERS
            ) {
                break;
            }
        }

        const executeRouteErase = () => {
            if (bestMatchIndex >= startIndex && bestDistance < ROUTE_ERASE_MAX_DISTANCE_METERS) {
                Taro.createMapContext('map').eraseLines({
                    lines: [{
                        id: ROUTE_POLYLINE_BASE_ID,
                        index: Math.max(0, bestMatchIndex - 1),
                        point: {
                            latitude: boundaryPoint.latitude,
                            longitude: boundaryPoint.longitude
                        },
                        clear: true
                    }]
                });
            }
            applyErasedRoutePoints(newPoints);
        };

        if (process.env.TARO_ENV !== 'weapp' || routeEraseReadyRef.current) {
            executeRouteErase();
            return;
        }
        pendingRouteEraseRef.current = [...newPoints];
        clearRouteEraseReadyTimer();
        const elapsedSincePolylineUpdate = lastPolylineUpdateAtRef.current > 0
            ? Math.max(0, Date.now() - lastPolylineUpdateAtRef.current)
            : 0;
        const waitMs = lastPolylineUpdateAtRef.current > 0
            ? Math.max(0, FIRST_ROUTE_ERASE_READY_DELAY_MS - elapsedSincePolylineUpdate)
            : FIRST_ROUTE_ERASE_READY_DELAY_MS;
        routeEraseReadyTimerRef.current = setTimeout(() => {
            routeEraseReadyTimerRef.current = null;
            routeEraseReadyRef.current = true;
            if (!pendingRouteEraseRef.current) {
                return;
            }
            const latestPoints = pendingRouteEraseRef.current;
            pendingRouteEraseRef.current = null;
            erasePassedRouteSegments(latestPoints);
        }, waitMs);
    };

    const getDisplayRoutePoints = () => {
        const isEraseMode = [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus);
        return isEraseMode
            ? routePoints
            : reducePoints(routePoints, resolveRoutePointLimit(deps.isAndroid));
    };

    const getPassedRoutePoints = () => {
        // 对齐 begin-route 当前主链：分享页不单独绘制贴着车辆的浅色已行驶轨迹，
        // 避免视觉上像车辆 marker 移动后残留一层浅蓝色影子。
        const shouldShowPassedRoute = false
            && deps.tripStatus === TripStatus.InService
            && Array.isArray(deps.vehicleMoveAlongPoints)
            && deps.vehicleMoveAlongPoints.length > 1;
        if (!shouldShowPassedRoute) {
            return [];
        }
        return reducePoints(
            deps.vehicleMoveAlongPoints || [],
            resolveRoutePointLimit(deps.isAndroid)
        );
    };

    const buildPolylineKey = (displayRoutePoints: any[]) => {
        const passedRoutePoints = getPassedRoutePoints();
        return [
            deps.shouldShowRoute ? 1 : 0,
            deps.statusRule.showWalking ? 1 : 0,
            buildPointsSignature(displayRoutePoints),
            buildPointsSignature(passedRoutePoints),
            buildPointsSignature(walkingPoints)
        ].join('|');
    };

    const buildPolylineLines = (displayRoutePoints: any[]) => {
        const lines: any[] = [];
        const passedRoutePoints = getPassedRoutePoints();
        if (passedRoutePoints.length > 1) {
            lines.push({
                id: PASSED_ROUTE_POLYLINE_ID,
                points: passedRoutePoints,
                color: '#D6D9DC',
                width: 10,
                clickable: false,
                dottedLine: false,
                arrowLine: false
            });
        }
        if (deps.shouldShowRoute && displayRoutePoints.length > 0) {
            lines.push({
                id: ROUTE_POLYLINE_BASE_ID,
                points: displayRoutePoints,
                color: '#59B688',
                width: 8,
                borderColor: '#3D8F66',
                borderWidth: 1,
                clickable: true,
                dottedLine: false,
                arrowLine: true
            });
        }
        if (deps.statusRule.showWalking && walkingPoints.length > 0) {
            lines.push({
                id: WALKING_POLYLINE_ID_OFFSET,
                points: walkingPoints,
                color: '#DFB369',
                width: deps.isAndroid ? 4 : 6,
                dottedLine: true,
                arrowLine: false
            });
        }
        return lines;
    };

    useEffect(() => {
        const displayRoutePoints = getDisplayRoutePoints();
        const nextPolylineKey = buildPolylineKey(displayRoutePoints);
        if (nextPolylineKey === polylineKeyRef.current) {
            return;
        }
        polylineKeyRef.current = nextPolylineKey;
        const shouldDrawRoute = deps.shouldShowRoute && displayRoutePoints.length > 0;
        const shouldDrawWalking = deps.statusRule.showWalking && walkingPoints.length > 0;
        if (shouldDrawRoute || shouldDrawWalking) {
            if (lastPolylineUpdateAtRef.current === 0) {
                routeEraseReadyRef.current = false;
                pendingRouteEraseRef.current = null;
                clearRouteEraseReadyTimer();
            }
            setPolyline(buildPolylineLines(displayRoutePoints));
            lastPolylineUpdateAtRef.current = Date.now();
            return;
        }
        if (![TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(deps.tripStatus)) {
            setPolyline([]);
        }
        lastPolylineUpdateAtRef.current = 0;
    }, [
        deps.isAndroid,
        deps.shouldShowRoute,
        deps.statusRule.showWalking,
        deps.tripStatus,
        deps.vehicleMoveAlongPoints,
        routePoints,
        walkingPoints
    ]);

    useEffect(() => {
        return () => {
            clearRouteEraseReadyTimer();
        };
    }, []);

    return {
        polyline,
        setPolyline,
        routePoints,
        setRoutePoints,
        walkingPoints,
        setWalkingPoints,
        walkingTime,
        setWalkingTime,
        polylineKeyRef,
        lastRoutePointRef,
        lastRouteUpdateAtRef,
        lastPolylineUpdateAtRef,
        currentFullRouteRef,
        lastErasedIndexRef,
        orderRouteInitializedRef,
        applyRoutePoints,
        reducePoints,
        resetRouteRuntimeState,
        erasePassedRouteSegments,
        buildPolylineKey,
        buildPolylineLines
    };
}
