<template>
  <!-- map container -->
  <div id="map" style="height: 100vh; background-color: transparent"></div>

  <!-- fullscreen video shown after 10 s of inactivity -->
  <div
    id="video-overlay"
    v-show="videoOverlayVisible"
    style="
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      z-index: 10000;
      background: #000;
      pointer-events: none;
    "
  ></div>
</template>

<script>
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import "../assets/style/main.css";
import tramRight from "../icons/tram-right.svg";
import tramLeft from "../icons/tram-left.svg";
import tramTopRight from "../icons/tram-top-right.svg";
import tramBottomRight from "../icons/tram-bottom-right.svg";
import tramTopLeft from "../icons/tram-top-left.svg";
import tramBottomLeft from "../icons/tram-bottom-left.svg";

import { API_URL, GEO_URL } from "../config";

const arrowTransforms = {
  right: "",
  left: "",
  "top-right": "translate(-25%, -50%)",
  "bottom-right": "translate(-25%, 50%)",
  "top-left": "translate(-100%, -50%)",
  "bottom-left": "translate(-100%, 50%)",
};

export default {
  name: "MainMap",
  data() {
    return {
      map: null,
      routeRenderer: null,
      routeLatlngs: [],
      stationMarkers: [],
      sightMarkers: [],
      tramMarker: null,
      fullPolyline: null,
      passedPolyline: null,
      tramDirection: "right",
      routeId: null,
      segmentLengths: [],
      cumulativeDistances: [],
      totalLength: 0,
      sightPollTimer: null,
      sightsData: [],
      nearestSightId: null,
      lastActivityTime: Date.now(),
      videoOverlayVisible: false,
      currentVideoSrc: null,
      lastVideoPreviewId: null,
      inactivityInterval: null,
    };
  },
  async mounted() {
    this.initializeMap();
    await this.fetchContext(); // obtain current routeId
    this.startTracking();
    this.setupActivityListeners();
    this.startInactivityCheck();
  },
  methods: {
    async fetchSights() {
      if (!this.routeId) return;
      try {
        const response = await fetch(`${API_URL}/route/${this.routeId}/sight`);
        const data = await response.json();
        console.log("Данные достопримечательностей:", data);

        if (Array.isArray(data)) {
          this.sightsData = data;
          const grouped = {};

          data.forEach((sight) => {
            const lat = sight.latitude.toFixed(2);
            const lng = sight.longitude.toFixed(2);
            const key = `${lat},${lng}`;
            if (!grouped[key]) grouped[key] = [];
            grouped[key].push(sight);
          });

          Object.entries(grouped).forEach(([key, sights]) => {
            const [lat, lng] = key.split(",").map(Number);
            const count = sights.length;

            const sightIcon = L.divIcon({
              className: "sight-icon",
              html: `
            <div style="position: relative; width: 53px; height: 49px;">
<svg xmlns="http://www.w3.org/2000/svg" width="53" height="49" fill="none" viewBox="0 0 53 49">
  <path fill="#A6A6A6" fill-rule="evenodd" d="M0 20.571h52.717v-3.933L26.367.642 0 16.638v3.933ZM26.366 4.4 4.753 17.503h43.21L26.367 4.4Z" clip-rule="evenodd"/>
  <path fill="#A6A6A6" d="M52.717 46.049H0V49h52.717v-2.95Zm-2.643-3.932H2.644v2.952h47.43v-2.952ZM9.463 22.245H5.498v17.421h3.965v-17.42Zm1.982 17.828h-7.93v1.151h7.93v-1.15Z"/>
  <path fill="#A6A6A6" d="M4.401 21.3c-.3 0-.54.237-.54.532 0 .295.24.531.54.531.3 0 .54-.236.54-.531h5.077c0 .295.24.531.54.531a.534.534 0 0 0 .542-.531.534.534 0 0 0-.541-.531H4.4Zm17.696.945h-3.965v17.421h3.965v-17.42Zm1.984 17.828h-7.93v1.151h7.93v-1.15Z"/>
  <path fill="#A6A6A6" d="M17.036 21.3c-.3 0-.54.237-.54.532 0 .295.24.531.54.531.3 0 .54-.236.54-.531h5.077c0 .295.24.531.54.531a.534.534 0 0 0 .541-.531.534.534 0 0 0-.54-.531h-6.158Zm17.705.945h-3.965v17.421h3.965v-17.42Zm1.983 17.828h-7.93v1.151h7.93v-1.15Z"/>
  <path fill="#A6A6A6" d="M29.68 21.3a.534.534 0 0 0-.541.532c0 .295.24.531.54.531.3 0 .541-.236.541-.531h5.077c0 .295.24.531.54.531.3 0 .54-.236.54-.531a.534.534 0 0 0-.54-.531H29.68Zm17.695.946H43.41v17.421h3.965v-17.42Zm1.985 17.827h-7.93v1.151h7.93v-1.15Z"/>
  <path fill="#A6A6A6" d="M42.313 21.3c-.3 0-.54.237-.54.532 0 .295.24.531.54.531.3 0 .54-.236.54-.531h5.077c0 .295.24.531.541.531.3 0 .54-.236.54-.531a.534.534 0 0 0-.54-.531h-6.158ZM26.848 10.065c0-.236-.233-.428-.518-.428-.286 0-.518.192-.518.428v.34c0 .28.232.508.518.508a.515.515 0 0 0 .518-.509v-.34Zm-7.338 2.228c-.24 0-.435.229-.435.517 0 .28.195.516.435.516h.901a.523.523 0 0 0 .526-.517.523.523 0 0 0-.526-.516h-.9Zm12.692 0c-.24 0-.436.229-.436.517 0 .28.195.516.436.516h.9a.523.523 0 0 0 .527-.517.523.523 0 0 0-.526-.516h-.901Zm-4.583-.487-.646-.406s-.075-.037-.12-.037H25.8a.23.23 0 0 0-.12.037l-.646.406a.219.219 0 0 0-.105.185v.472c0 .044.015.088.038.125l.563.82c.045.066.053.14.023.213l-.609 1.358s-.022.059-.022.088v1.35a.22.22 0 0 0 .225.222h2.343a.22.22 0 0 0 .225-.221v-1.35s0-.06-.022-.09l-.608-1.357a.22.22 0 0 1 .022-.214l.563-.819s.038-.081.038-.125v-.472a.21.21 0 0 0-.105-.185h.015Z"/>
</svg>
              ${
                count > 1
                  ? `<div style="position: absolute; top: -10px; right: -10px; background: #896F58; color: white; font-weight: bold; font-size: 16px; padding: 2px 10px; border-radius: 50%;">${count}</div>`
                  : ""
              }
            </div>
          `,
              iconSize: [53, 49],
              iconAnchor: [26, 24],
            });

            const marker = L.marker([lat, lng], {
              icon: sightIcon,
              pane: "markerPane",
            }).addTo(this.map);

            this.sightMarkers.push(marker);
          });
        }
      } catch (error) {
        console.error("Ошибка при получении достопримечательностей:", error);
      }
      // Stop polling once at least one sight marker exists
      if (this.sightMarkers.length > 0 && this.sightPollTimer) {
        clearInterval(this.sightPollTimer);
        this.sightPollTimer = null;
      }
    },

    // ── Poll fetchSights every 3 s until at least one sight is on the map ──
    startSightPolling() {
      if (this.sightPollTimer) return; // already polling
      this.sightPollTimer = setInterval(() => {
        this.fetchSights();
        if (this.sightMarkers.length > 0) {
          clearInterval(this.sightPollTimer);
          this.sightPollTimer = null;
        }
      }, 3000);
    },

    stopSightPolling() {
      if (this.sightPollTimer) {
        clearInterval(this.sightPollTimer);
        this.sightPollTimer = null;
      }
    },

    // ───────────────── user‑inactivity & video overlay helpers ──────────────────
    setupActivityListeners() {
      ["mousemove", "mousedown", "keydown", "touchstart", "scroll"].forEach(
        (evt) => window.addEventListener(evt, this.resetInactivity)
      );
    },
    resetInactivity() {
      this.lastActivityTime = Date.now();
      if (this.videoOverlayVisible) this.hideVideoOverlay();
    },
    startInactivityCheck() {
      if (this.inactivityInterval) return;
      this.inactivityInterval = setInterval(() => {
        if (Date.now() - this.lastActivityTime >= 300_000) {
          this.showNearestSightVideo();
        }
      }, 1000);
    },
    stopInactivityCheck() {
      if (this.inactivityInterval) {
        clearInterval(this.inactivityInterval);
        this.inactivityInterval = null;
      }
    },
    async showNearestSightVideo() {
      if (!this.sightsData.length) return;

      // choose closest sight that has a preview
      let sight = this.sightsData.find((s) => s.id === this.nearestSightId);
      if (!sight || !sight.video_preview) {
        sight = this.sightsData.find((s) => s.video_preview);
      }
      if (!sight || !sight.video_preview) return;

      if (this.lastVideoPreviewId === sight.video_preview) {
        this.videoOverlayVisible = true;
        return; // already showing it
      }

      const src = `${API_URL}/media/${sight.video_preview}/download`;
      this.replaceVideo(src, sight.video_preview);
    },
    replaceVideo(src, previewId) {
      const overlay = document.getElementById("video-overlay");
      if (!overlay) return;

      const vid = document.createElement("video");
      vid.src = src;
      vid.autoplay = true;
      vid.muted = true;
      vid.loop = true;
      vid.playsInline = true;
      vid.style.cssText = "width:100%;height:100%;object-fit:cover;";

      vid.addEventListener("canplay", () => {
        [...overlay.children].forEach((child) => {
          if (child !== vid) overlay.removeChild(child);
        });
        this.videoOverlayVisible = true;
        this.lastVideoPreviewId = previewId;
      });

      overlay.appendChild(vid);
    },
    hideVideoOverlay() {
      this.videoOverlayVisible = false;
      const overlay = document.getElementById("video-overlay");
      if (overlay) overlay.innerHTML = "";
      this.lastVideoPreviewId = null;
    },
    // ─────────────────────────────────────────────────────────────────────────────

    initializeMap() {
      this.routeRenderer = L.canvas({ padding: 0.5 });

      this.map = L.map("map", {
        zoomControl: false,
        attributionControl: false,
        zoomAnimation: false,
        inertia: false,
        preferCanvas: true,
        renderer: this.routeRenderer,
        maxZoom: 18,
        minZoom: 10,
        zoomSnap: 0,
        zoomDelta: 0.1,
        wheelPxPerZoomLevel: 50,
      });
      this.map.whenReady(() => {
        const mapPane = this.map.getPane("mapPane") || this.map.getContainer();
        mapPane.style.transition = "none";
      });
      this.map.createPane("routePane");
      this.map.getPane("routePane").style.zIndex = 400;
      this.map.setView([59.91776, 30.279188], 13);
      this.map.createPane("tramPane");
      this.map.getPane("tramPane").style.zIndex = 600;
    },

    async fetchRoute() {
      if (!this.routeId) return;
      fetch(`${API_URL}/route/${this.routeId}`)
        .then((response) => response.json())
        .then((data) => {
          if (data.path && Array.isArray(data.path)) {
            this.routeLatlngs = data.path.map((coord) => [coord[0], coord[1]]);
            // ── Pre-compute cumulative distances for distance-based progress ──
            this.segmentLengths = [];
            this.cumulativeDistances = [0];
            for (let i = 1; i < this.routeLatlngs.length; i++) {
              const prev = L.latLng(this.routeLatlngs[i - 1]);
              const curr = L.latLng(this.routeLatlngs[i]);
              const segLen = prev.distanceTo(curr); // metres
              this.segmentLengths.push(segLen);
              this.cumulativeDistances.push(
                this.cumulativeDistances[i - 1] + segLen
              );
            }
            this.totalLength =
              this.cumulativeDistances[this.cumulativeDistances.length - 1] ||
              0;
            if (
              typeof data.scale_min === "number" &&
              typeof data.scale_max === "number"
            ) {
              this.map.setMinZoom(data.scale_min);
              this.map.setMaxZoom(data.scale_max);
              console.log("Установлены масштабы:", {
                minZoom: this.map.getMinZoom(),
                maxZoom: this.map.getMaxZoom(),
              });
            }
            if (data.center_latitude && data.center_longitude) {
              console.log("Установка центра карты:", {
                latitude: data.center_latitude,
                longitude: data.center_longitude,
              });
              this.map.setView(
                [data.center_latitude, data.center_longitude],
                this.map.getMaxZoom()
              );
              console.log(
                "Координаты центра установлены:",
                this.map.getCenter()
              );
              if (typeof data.rotate === "number") {
                if (!this.map.rotation) {
                  this.map.rotation = { angle: 0 };
                }
                this.map.rotation.angle = data.rotate;
                console.warn(
                  "Leaflet не поддерживает поворот карты по умолчанию. Для этого нужна сторонняя библиотека, например Leaflet-RotatedMarker или Leaflet-bearing."
                );
              }
            }

            const routeBounds = L.latLngBounds(this.routeLatlngs);
            if (data.center_latitude && data.center_longitude) {
              console.log("Установка центра карты:", {
                latitude: data.center_latitude,
                longitude: data.center_longitude,
              });
              this.map.setView(
                [data.center_latitude, data.center_longitude],
                14
              );
              console.log(
                "Координаты центра установлены:",
                this.map.getCenter()
              );
            } else {
              this.map.fitBounds(routeBounds);
            }
            this.fetchStations();
          }
        })
        .catch((error) => {
          console.error("Ошибка при получении маршрута:", error);
        });
    },

    async fetchStations() {
      if (!this.routeId) return;
      this.stationMarkers.forEach(({ marker }) => {
        if (this.map.hasLayer(marker)) {
          this.map.removeLayer(marker);
        }
      });
      this.stationMarkers = [];

      try {
        const [ruStations, enStations] = await Promise.all([
          fetch(`${API_URL}/route/${this.routeId}/station`).then((r) =>
            r.json()
          ),
          fetch(`${API_URL}/route/${this.routeId}/station?lang=en`).then((r) =>
            r.json()
          ),
        ]);

        const enById = {};
        if (Array.isArray(enStations)) {
          enStations.forEach((st) => {
            enById[st.id] = st.name;
          });
        }

        if (Array.isArray(ruStations)) {
          ruStations.forEach((station, index) => {
            const circleMarker = L.circleMarker(
              [station.latitude, station.longitude],
              {
                radius: 7,
                color: "black",
                fillColor: "red",
                fillOpacity: 1,
                weight: 2,
                opacity: 1,
                fill: true,
                pane: "markerPane",
              }
            );

            const enName = enById[station.id] || "";

            circleMarker
              .bindTooltip(
                `<div class="station-label-wrapper">
                   <div class="station-name-ru">${station.name}</div>
                   <div class="station-name-en">${enName}</div>
                 </div>`,
                {
                  permanent: true,
                  direction: "center",
                  offset: [station.offset_x || 0, station.offset_y || 0],
                  className: "station-label-no-bg",
                }
              )
              .openTooltip();

            circleMarker.addTo(this.map);

            this.stationMarkers.push({
              marker: circleMarker,
              station: {
                ...station,
                index,
                latitude: station.latitude,
                longitude: station.longitude,
                offset_x: station.offset_x || 0,
                offset_y: station.offset_y || 0,
              },
            });
          });
        }
      } catch (error) {
        console.error("Ошибка при получении станций маршрута:", error);
      }
    },

    chooseTramDirection(latlng) {
      if (!this.map) return "right";

      const tramPt = this.map.latLngToLayerPoint(latlng);

      const candidates = {
        right: [1, 0],
        left: [-1, 0],
        "top-right": [1, -1],
        "bottom-right": [1, 1],
        "top-left": [-1, -1],
        "bottom-left": [-1, 1],
      };

      const toPt = (ll) => this.map.latLngToLayerPoint(ll);

      const routePts = this.routeLatlngs.map(toPt);

      let bestDir = "right";
      let bestScore = Number.POSITIVE_INFINITY;

      Object.entries(candidates).forEach(([dir, vec]) => {
        const len = Math.hypot(vec[0], vec[1]);
        const unit = [vec[0] / len, vec[1] / len];

        let score = 0;

        [30, 60, 90].forEach((d) => {
          const probe = L.point(tramPt.x + unit[0] * d, tramPt.y + unit[1] * d);

          this.stationMarkers.forEach(({ marker }) => {
            if (
              probe.distanceTo(toPt(marker.getLatLng())) < 25 /* px threshold */
            )
              score += 3;
          });
          this.sightMarkers.forEach((m) => {
            if (probe.distanceTo(toPt(m.getLatLng())) < 25) score += 3;
          });

          routePts.forEach((rpt) => {
            if (probe.distanceTo(rpt) < 20) score += 1;
          });
        });

        if (score < bestScore) {
          bestScore = score;
          bestDir = dir;
        }
      });

      return bestDir;
    },

    createTramIcon(direction = "right") {
      const svgMap = {
        right: tramRight,
        left: tramLeft,
        "top-right": tramTopRight,
        "bottom-right": tramBottomRight,
        "top-left": tramTopLeft,
        "bottom-left": tramBottomLeft,
      };

      const svgSrc = svgMap[direction] || tramRight;

      const arrowTransform = arrowTransforms[direction] || "";

      const circleOnRight = direction.startsWith("left");
      const circleStyleSide = circleOnRight ? "right: 0;" : "left: 0;";
      const anchorX = circleOnRight ? 121 : 19;

      console.log(arrowTransform, direction, svgSrc);

      return L.divIcon({
        className: "tram-icon",
        html: `
          <div style="position: relative; width: 140px; height: 140px;">
            <!-- arrow SVG -->
            <img src="${svgSrc}"
                 style="position:absolute; top:50%; ${
                   circleOnRight ? "left:0;" : "right:0;"
                 } transform:translateY(-50%) ${arrowTransform}; width:112px; height:82px;" />
            <!-- tram location circle -->
            <div style="
              position: absolute;
              top: 50%;
              ${circleStyleSide}
              width: 28px;
              height: 28px;
              background: yellow;
              border: 5px solid black;
              border-radius: 50%;
              transform: translate(5px, -50%);
              z-index: 2;"></div>
          </div>
        `,
        iconSize: [140, 140],
        iconAnchor: [anchorX, 70],
      });
    },

    startTracking() {
      setInterval(this.updateTramPosition, 500);
    },

    async fetchContext() {
      try {
        const response = await fetch(`${GEO_URL}/v1/geolocation/context`);
        const data = await response.json();
        const ctxRouteId =
          (data && data.routeId) ||
          (data.routeProgress && data.routeProgress.routeId) ||
          null;

        if (ctxRouteId && ctxRouteId !== this.routeId) {
          this.routeId = ctxRouteId;
        }

        const ctxNearest =
          data.nearestSightId ||
          (data.routeProgress && data.routeProgress.nearestSightId);
        if (ctxNearest) this.nearestSightId = ctxNearest;
      } catch (error) {
        console.error("Ошибка при получении контекста геолокации:", error);
      }
    },

    async updateTramPosition() {
      try {
        const response = await fetch(`${GEO_URL}/v1/geolocation/context`);
        const data = await response.json();
        if (
          data.routeId &&
          data.routeId !== 0 &&
          data.routeId !== this.routeId
        ) {
          this.routeId = data.routeId;
        }
        // console.log("Текущие координаты трамвая:", data.currentCoordinates);

        const ctxNearest =
          data.nearestSightId ||
          (data.routeProgress && data.routeProgress.nearestSightId);
        if (ctxNearest) this.nearestSightId = ctxNearest;

        const { percentageCompleted } = data.routeProgress;

        if (this.totalLength === 0) return;

        // Дистанция, пройденная трамваем (м)
        const targetDistance = percentageCompleted * this.totalLength;

        // Определяем сегмент маршрута, в котором находится трамвай
        let segIdx = 0;
        while (
          segIdx < this.cumulativeDistances.length - 1 &&
          this.cumulativeDistances[segIdx + 1] < targetDistance
        ) {
          segIdx++;
        }

        const segStart = this.routeLatlngs[segIdx];
        const segEnd =
          this.routeLatlngs[Math.min(segIdx + 1, this.routeLatlngs.length - 1)];
        const segStartDist = this.cumulativeDistances[segIdx];
        const segLen = this.cumulativeDistances[segIdx + 1] - segStartDist || 1;

        // Интерполируем точку на сегменте
        const t = segLen === 0 ? 0 : (targetDistance - segStartDist) / segLen;
        const tramLatLng = [
          segStart[0] + (segEnd[0] - segStart[0]) * t,
          segStart[1] + (segEnd[1] - segStart[1]) * t,
        ];

        // Формируем координаты пройденной и оставшейся части
        const passedCoords = this.routeLatlngs.slice(0, segIdx + 1);
        passedCoords.push(tramLatLng);
        const fullCoords = [tramLatLng, ...this.routeLatlngs.slice(segIdx + 1)];

        // ── Update / create progress polylines without flicker ──
        if (!this.passedPolyline) {
          this.passedPolyline = L.polyline(passedCoords, {
            color: "red",
            weight: 7,
            pane: "routePane",
            renderer: this.routeRenderer,
          }).addTo(this.map);
        } else {
          this.passedPolyline.setLatLngs(passedCoords);
        }

        if (!this.fullPolyline) {
          this.fullPolyline = L.polyline(fullCoords, {
            color: "white",
            weight: 7,
            pane: "routePane",
            renderer: this.routeRenderer,
          }).addTo(this.map);
        } else {
          this.fullPolyline.setLatLngs(fullCoords);
        }

        if (this.tramMarker && !this.map.hasLayer(this.tramMarker)) {
          this.tramMarker.addTo(this.map);
        }

        this.stationMarkers.forEach(({ marker, station }) => {
          const stationIndex = this.routeLatlngs.reduce(
            (closestIdx, latlng, idx) => {
              const dist = L.latLng(latlng).distanceTo([
                station.latitude,
                station.longitude,
              ]);
              const closestDist = L.latLng(
                this.routeLatlngs[closestIdx]
              ).distanceTo([station.latitude, station.longitude]);
              return dist < closestDist ? idx : closestIdx;
            },
            0
          );

          const stationPassed =
            this.cumulativeDistances[stationIndex] <= targetDistance;
          const color = stationPassed ? "red" : "white";

          marker.setStyle({
            color: "black",
            fillColor: color,
            fillOpacity: 1,
            radius: 8,
            weight: 4,
          });

          if (!this.map.hasLayer(marker)) {
            marker.addTo(this.map);
          }
        });

        const direction = this.chooseTramDirection(tramLatLng);

        if (!this.tramMarker && tramLatLng[0] && tramLatLng[1]) {
          this.tramMarker = L.marker(tramLatLng, {
            icon: this.createTramIcon(direction),
            pane: "tramPane",
          }).addTo(this.map);
          this.tramDirection = direction;
        } else {
          if (direction !== this.tramDirection) {
            this.tramMarker.setIcon(this.createTramIcon(direction));
            this.tramDirection = direction;
          }
          this.tramMarker.setLatLng(tramLatLng);
        }
      } catch (error) {
        console.error("Ошибка при получении геолокации трамвая:", error);
      }
    },
  },
  watch: {
    // When routeId becomes available or changes, (re)load dependent data
    routeId(newVal, oldVal) {
      if (newVal && newVal !== oldVal) {
        this.fetchRoute();
        this.fetchSights();
        this.fetchStations();
        this.startSightPolling();
      }
    },
  },
  beforeUnmount() {
    if (this.sightPollTimer) {
      clearInterval(this.sightPollTimer);
      this.sightPollTimer = null;
    }
    this.stopInactivityCheck();
    ["mousemove", "mousedown", "keydown", "touchstart", "scroll"].forEach(
      (evt) => window.removeEventListener(evt, this.resetInactivity)
    );
  },
};
</script>
