import { makeAutoObservable, runInAction } from "mobx";
import {
  getRouteSights,
  getRouteStations,
  getRoute,
  getCarrier,
  getCity,
  getArticle,
  getArticleMedia,
  getSightStations,
  getStationSights,
  getSightArticlesIds,
} from "./api";
import {
  GetContextResponse,
  GetRouteSightsResponse,
  GetRouteStationsResponse,
  GetMediaResponse,
  GetRouteResponse,
  GetCarrierResponse,
  GetCityResponse,
  GetSightArticleResponse,
} from "./types";
// @ts-ignore
import { orderStationsByRoute } from "../../utils/routeStationsUtils";
import { resamplePath } from "../../utils/animationUtils";
import { colorStore } from "../../stores/ColorStore";

function hexToRgbString(hex: string): string | null {
  const clean = hex.trim().replace(/^#/, "");
  const full = clean.length === 3 ? clean.split("").map((c) => c + c).join("") : clean;
  if (full.length !== 6) return null;
  const r = parseInt(full.slice(0, 2), 16);
  const g = parseInt(full.slice(2, 4), 16);
  const b = parseInt(full.slice(4, 6), 16);
  return `${r}, ${g}, ${b}`;
}

function darkenHex(hex: string, amount: number): string {
  const rgb = hexToRgbString(hex);
  if (!rgb) return hex;
  const [r, g, b] = rgb.split(",").map(Number);
  const factor = 1 - amount;
  const dr = Math.round(r * factor);
  const dg = Math.round(g * factor);
  const db = Math.round(b * factor);
  return `#${dr.toString(16).padStart(2, "0")}${dg.toString(16).padStart(2, "0")}${db.toString(16).padStart(2, "0")}`;
}

function lightenRgbString(hex: string, amount: number): string | null {
  const rgb = hexToRgbString(hex);
  if (!rgb) return null;
  const [r, g, b] = rgb.split(",").map(Number);
  const lr = Math.round(r + (255 - r) * amount);
  const lg = Math.round(g + (255 - g) * amount);
  const lb = Math.round(b + (255 - b) * amount);
  return `${lr}, ${lg}, ${lb}`;
}

function applyCarrierColors(carrier: { main_color?: string; left_color?: string; right_color?: string }) {
  const mainColor = carrier.main_color || "#006f3a";
  const leftColor = carrier.left_color || "#006f3a";
  const rightColor = carrier.right_color || "#006f3a";
  const mainDark = darkenHex(mainColor, 0.3);

  document.documentElement.style.setProperty("--carrier-main", mainColor);
  document.documentElement.style.setProperty("--carrier-main-rgb", hexToRgbString(mainColor) ?? "0, 111, 58");
  document.documentElement.style.setProperty("--carrier-main-dark", mainDark);
  document.documentElement.style.setProperty("--carrier-left", leftColor);
  document.documentElement.style.setProperty("--carrier-left-rgb", hexToRgbString(leftColor) ?? "0, 111, 58");
  document.documentElement.style.setProperty("--carrier-right", rightColor);
  document.documentElement.style.setProperty("--carrier-right-rgb", hexToRgbString(rightColor) ?? "0, 111, 58");
  document.documentElement.style.setProperty("--carrier-right-menu-rgb", lightenRgbString(rightColor, 0.38) ?? "179, 165, 152");
}

class ApiStore {
  isLoading = true;
  loadingStatus: string | null = null;
  loadingProgress: { current: number; total: number } | null = null;

  routeId: string | null = null;

  context: GetContextResponse | null = null;

  routeSights: GetRouteSightsResponse | null = null;
  routeSightsEn: GetRouteSightsResponse | null = null;
  routeSightsZh: GetRouteSightsResponse | null = null;

  routeStations: GetRouteStationsResponse | null = null;
  routeStationsEn: GetRouteStationsResponse | null = null;
  routeStationsZh: GetRouteStationsResponse | null = null;
  orderedRouteStations: GetRouteStationsResponse | null = null;

  sightArticles: Map<string, GetSightArticleResponse> = new Map();
  sightArticlesEn: Map<string, GetSightArticleResponse> = new Map();
  sightArticlesZh: Map<string, GetSightArticleResponse> = new Map();

  sightArticlesIds: Map<number, number[]> = new Map();

  sightStationsCache: Map<string, GetRouteStationsResponse> = new Map();
  stationSightsCache: Map<string, GetRouteSightsResponse> = new Map();

  route: GetRouteResponse | null = null;
  carrier: GetCarrierResponse | null = null;
  city: GetCityResponse | null = null;

  positionIndex = 0;
  private positionInterval: ReturnType<typeof setInterval> | null = null;

  simulationSpeed = 1;
  simulationDirection: 1 | -1 = 1;
  simulationPaused = false;
  simulationInstantMove = false;
  showHitboxes = false;

  constructor() {
    makeAutoObservable(this);
  }

  setRouteId = (id: string) => {
    this.routeId = id;
  };

  setIsLoading = (isLoading: boolean) => {
    this.isLoading = isLoading;
    if (!isLoading) {
      this.loadingStatus = null;
      this.loadingProgress = null;
    }
  };

  setLoadingStatus = (status: string | null) => {
    this.loadingStatus = status;
  };

  setLoadingProgress = (current: number, total: number) => {
    this.loadingProgress = { current, total };
  };

  getRoute = async () => {
    const route = await getRoute(this.routeId!);
    if (route.path && route.path.length > 1) {
      // Рассчитываем общую дистанцию для выбора адекватного шага ресемплинга
      let totalDist = 0;
      for (let i = 0; i < route.path.length - 1; i++) {
        const p1 = route.path[i];
        const p2 = route.path[i + 1];
        totalDist += Math.sqrt(
          Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2)
        );
      }
      // Хотим иметь примерно 2000 точек для равномерности и плавности
      const segmentLength = totalDist / 2000;
      if (segmentLength > 0) {
        route.path = resamplePath(route.path as [number, number][], segmentLength);
      }
    }
    runInAction(() => {
      this.route = route;
    });
    this.updateOrderedRouteStations();
  };

  getCarrier = async () => {
    this.carrier = await getCarrier(this.route!.carrier_id!);
    applyCarrierColors(this.carrier);
    if (this.carrier.main_color) {
      colorStore.setMainColor(this.carrier.main_color);
    }
  };

  getCity = async () => {
    this.city = await getCity(this.carrier!.city_id!);
  };

  getRouteSights = async () => {
    this.routeSights = await getRouteSights(this.routeId!);
    this.routeSightsEn = await getRouteSights(this.routeId!, "en");
    this.routeSightsZh = await getRouteSights(this.routeId!, "zh");
  };

  getRouteStations = async () => {
    this.routeStations = await getRouteStations(this.routeId!);
    this.routeStationsEn = await getRouteStations(this.routeId!, "en");
    this.routeStationsZh = await getRouteStations(this.routeId!, "zh");
    this.updateOrderedRouteStations();
  };

  updateOrderedRouteStations = () => {
    if (
      !this.routeStations ||
      !this.route?.path ||
      !this.context?.startStopId ||
      !this.context?.endStopId
    ) {
      runInAction(() => {
        this.orderedRouteStations = null;
      });
      return;
    }

    const ordered = orderStationsByRoute(
      this.routeStations,
      this.route.path,
      this.context.startStopId,
      this.context.endStopId
    );

    runInAction(() => {
      this.orderedRouteStations = ordered;
    });
  };

  setSimulationSpeed = (speed: number) => {
    this.simulationSpeed = Math.max(0.25, Math.min(5, speed));
    if (this.positionInterval) {
      clearInterval(this.positionInterval);
      const intervalMs = Math.round(3000 / this.simulationSpeed);
      this.positionInterval = setInterval(() => {
        if (!this.simulationPaused) {
          this.updateSimulatedPosition();
        }
      }, intervalMs);
    }
  };

  setSimulationDirection = (direction: 1 | -1) => {
    this.simulationDirection = direction;
  };

  toggleSimulationDirection = () => {
    this.simulationDirection = this.simulationDirection === 1 ? -1 : 1;
  };

  setSimulationPaused = (paused: boolean) => {
    this.simulationPaused = paused;
  };

  toggleSimulationPaused = () => {
    this.simulationPaused = !this.simulationPaused;
  };

  toggleSimulationInstantMove = () => {
    this.simulationInstantMove = !this.simulationInstantMove;
  };

  toggleShowHitboxes = () => {
    this.showHitboxes = !this.showHitboxes;
  };

  startPositionSimulation = () => {
    if (this.positionInterval) return;

    this.updateSimulatedPosition();

    const intervalMs = Math.round(3000 / this.simulationSpeed);
    this.positionInterval = setInterval(() => {
      if (!this.simulationPaused) {
        this.updateSimulatedPosition();
      }
    }, intervalMs);
  };

  stopPositionSimulation = () => {
    if (this.positionInterval) {
      clearInterval(this.positionInterval);
      this.positionInterval = null;
    }
  };

  private updateSimulatedPosition = () => {
    if (!this.route?.path?.length) return;

    const path = this.route.path;
    const step = Math.max(1, Math.floor(path.length / 120));
    this.positionIndex = this.positionIndex + step * this.simulationDirection;
    if (this.positionIndex >= path.length) this.positionIndex = 0;
    if (this.positionIndex < 0) this.positionIndex = path.length - 1;

    const [lat, lon] = path[this.positionIndex];

    let nearestStationId = this.context?.nearestStationId ?? null;
    if (this.routeStations?.length) {
      let minDist = Infinity;
      for (const station of this.routeStations) {
        const d =
          Math.pow(station.latitude - lat, 2) +
          Math.pow(station.longitude - lon, 2);
        if (d < minDist) {
          minDist = d;
          nearestStationId = String(station.id);
        }
      }
    }

    let nearestSightId = this.context?.nearestSightId ?? null;
    if (this.routeSights?.length) {
      let minDist = Infinity;
      for (const sight of this.routeSights) {
        const d =
          Math.pow(sight.latitude - lat, 2) +
          Math.pow(sight.longitude - lon, 2);
        if (d < minDist) {
          minDist = d;
          nearestSightId = String(sight.id);
        }
      }
    }

    const percentageCompleted =
      (this.positionIndex / path.length) * 100;

    const startStopId =
      this.routeStations?.[0]
        ? String(this.routeStations[0].id)
        : this.context?.startStopId ?? "";
    const endStopId =
      this.routeStations?.length
        ? String(this.routeStations[this.routeStations.length - 1].id)
        : this.context?.endStopId ?? "";

    runInAction(() => {
      this.context = {
        routeId: this.routeId!,
        routeNumber: this.route?.route_number ?? "",
        currentCoordinates: { latitude: lat, longitude: lon },
        rawCoordinates: { latitude: lat, longitude: lon },
        startStopId,
        endStopId,
        nearestSightId: nearestSightId ?? "",
        nearestStationId: nearestStationId ?? undefined,
        routeProgress: {
          startStopId,
          endStopId: nearestStationId ?? endStopId,
          percentageCompleted,
        },
        maintenanceModeOn: false,
      } as GetContextResponse;

      if (!this.orderedRouteStations) {
        this.updateOrderedRouteStations();
      }
    });
  };

  private getArticle = async (articleId: number, lang: string) => {
    const article = await getArticle(articleId, lang);

    const isArticleExists = this.sightArticles.has(
      articleId.toString() + "_" + "ru"
    );

    let media: GetMediaResponse;
    if (isArticleExists) {
      const ruArticle = this.sightArticles.get(
        articleId.toString() + "_" + "ru"
      );
      media = ruArticle?.media!;
    } else {
      media = await getArticleMedia(articleId);
    }

    runInAction(() => {
      if (lang === "ru") {
        this.sightArticles.set(articleId.toString() + "_" + "ru", {
          id: articleId,
          heading: article.heading,
          body: article.body,
          media,
        });
      } else if (lang === "en") {
        this.sightArticlesEn.set(articleId.toString() + "_" + "en", {
          id: articleId,
          heading: article.heading,
          body: article.body,
          media,
        });
      } else if (lang === "zh") {
        this.sightArticlesZh.set(articleId.toString() + "_" + "zh", {
          id: articleId,
          heading: article.heading,
          body: article.body,
          media,
        });
      }
    });
  };

  private getSightArticles = async (sightId: number) => {
    const leftArticle = this.routeSights?.find(
      (sight) => sight.id === sightId
    )?.left_article;

    const rightArticles = await getSightArticlesIds(sightId);

    runInAction(() => {
      this.sightArticlesIds.set(sightId, rightArticles);
    });

    if (leftArticle) {
      this.setLoadingStatus(`Загрузка статьи ${leftArticle} (ru)...`);
      await this.getArticle(leftArticle, "ru");
      this.setLoadingStatus(`Загрузка статьи ${leftArticle} (en)...`);
      await this.getArticle(leftArticle, "en");
      this.setLoadingStatus(`Загрузка статьи ${leftArticle} (zh)...`);
      await this.getArticle(leftArticle, "zh");
    }

    for (const article of rightArticles) {
      this.setLoadingStatus(`Загрузка статьи ${article} (ru)...`);
      await this.getArticle(article, "ru");
      this.setLoadingStatus(`Загрузка статьи ${article} (en)...`);
      await this.getArticle(article, "en");
      this.setLoadingStatus(`Загрузка статьи ${article} (zh)...`);
      await this.getArticle(article, "zh");
    }
  };

  getAllSightArticles = async () => {
    const totalSights = this.routeSights!.length;
    let currentSight = 0;

    for (const sight of this.routeSights!) {
      currentSight++;
      this.setLoadingStatus(
        `Загрузка статей для достопримечательности ${currentSight}/${totalSights}...`
      );
      this.setLoadingProgress(currentSight, totalSights);
      await this.getSightArticles(sight.id);
    }
  };

  getSightStations = async (sightId: number, lang: string = "ru") => {
    const cacheKey = `${sightId}_${lang}`;

    if (this.sightStationsCache.has(cacheKey)) {
      return this.sightStationsCache.get(cacheKey)!;
    }

    const stations = await getSightStations(sightId, lang);

    runInAction(() => {
      this.sightStationsCache.set(cacheKey, stations);
    });

    return stations;
  };

  getStationSights = async (stationId: number, lang: string = "ru") => {
    const cacheKey = `${stationId}_${lang}`;

    if (this.stationSightsCache.has(cacheKey)) {
      return this.stationSightsCache.get(cacheKey)!;
    }

    const sights = await getStationSights(stationId, lang);

    runInAction(() => {
      this.stationSightsCache.set(cacheKey, sights);
    });

    return sights;
  };

  preloadSightStationsAndStationSights = async () => {
    if (!this.routeSights || !this.routeStations) return;

    const languages = ["ru", "en", "zh"];

    for (const sight of this.routeSights) {
      for (const lang of languages) {
        try {
          await this.getSightStations(sight.id, lang);
        } catch (error) {
          console.warn(
            `Failed to preload stations for sight ${sight.id} (${lang}):`,
            error
          );
        }
      }
    }

    for (const station of this.routeStations) {
      for (const lang of languages) {
        try {
          await this.getStationSights(station.id, lang);
        } catch (error) {
          console.warn(
            `Failed to preload sights for station ${station.id} (${lang}):`,
            error
          );
        }
      }
    }
  };
}

export const apiStore = new ApiStore();
