import { languageInstance } from "@shared";
import { makeAutoObservable } from "mobx";

interface ApiRoute {
  id: number;
  route_number: string;
  path: [number, number][];
}

interface ApiStation {
  id: number;
  name: string;
  latitude: number;
  longitude: number;
}

interface ApiSight {
  id: number;
  name: string;
  description: string;
  latitude: number;
  longitude: number;
}

const COORDINATE_PRECISION_TOLERANCE = 1e-9;

const arePathsEqual = (
  path1: [number, number][],
  path2: [number, number][]
): boolean => {
  if (path1.length !== path2.length) {
    return false;
  }
  for (let i = 0; i < path1.length; i++) {
    if (
      Math.abs(path1[i][0] - path2[i][0]) > COORDINATE_PRECISION_TOLERANCE ||
      Math.abs(path1[i][1] - path2[i][1]) > COORDINATE_PRECISION_TOLERANCE
    ) {
      return false;
    }
  }
  return true;
};

class MapStore {
  constructor() {
    makeAutoObservable(this);
  }

  routes: ApiRoute[] = [];
  stations: ApiStation[] = [];
  sights: ApiSight[] = [];

  getRoutes = async () => {
    const response = await languageInstance("ru").get("/route");

    const routesIds = response.data.map((route: any) => route.id);
    for (const id of routesIds) {
      const route = await languageInstance("ru").get(`/route/${id}`);
      this.routes.push({
        id: route.data.id,
        route_number: route.data.route_number,
        path: route.data.path,
      });
    }
    const mappedRoutes: ApiRoute[] = response.data.map((route: any) => ({
      id: route.id,
      route_number: route.route_number,
      path: route.path,
    }));
    this.routes = mappedRoutes.sort((a, b) =>
      a.route_number.trim().localeCompare(b.route_number.trim())
    );
  };

  getStations = async () => {
    const stations = await languageInstance("ru").get("/station");
    this.stations = stations.data.map((station: any) => ({
      id: station.id,
      name: station.name,
      latitude: station.latitude,
      longitude: station.longitude,
    }));
  };

  getSights = async () => {
    const sights = await languageInstance("ru").get("/sight");
    this.sights = sights.data.map((sight: any) => ({
      id: sight.id,
      name: sight.name,
      description: sight.description,
      latitude: sight.latitude,
      longitude: sight.longitude,
    }));
  };

  deleteRecourse = async (recourse: string, id: number) => {
    await languageInstance("ru").delete(`/${recourse}/${id}`);
    if (recourse === "route") {
      this.routes = this.routes.filter((route) => route.id !== id);
    } else if (recourse === "station") {
      this.stations = this.stations.filter((station) => station.id !== id);
    } else if (recourse === "sight") {
      this.sights = this.sights.filter((sight) => sight.id !== id);
    }
  };

  handleSave = async (json: string) => {
    const newSights: any[] = [];
    const newRoutes: any[] = [];
    const newStations: any[] = [];
    const updatedSights: any[] = [];
    const updatedRoutes: any[] = [];
    const updatedStations: any[] = [];

    const parsedJSON = JSON.parse(json);

    for (const feature of parsedJSON.features) {
      const { geometry, properties, id } = feature;
      const idParts = String(id).split("-");

      if (idParts.length > 1) {
        const featureType = idParts[0];
        const numericId = parseInt(idParts[1], 10);
        if (isNaN(numericId)) continue;

        if (featureType === "station") {
          const originalStation = this.stations.find((s) => s.id === numericId);
          if (!originalStation) continue;

          const currentStation = {
            name: properties.name || "",
            latitude: geometry.coordinates[1],
            longitude: geometry.coordinates[0],
          };

          if (
            originalStation.name !== currentStation.name ||
            Math.abs(originalStation.latitude - currentStation.latitude) >
              COORDINATE_PRECISION_TOLERANCE ||
            Math.abs(originalStation.longitude - currentStation.longitude) >
              COORDINATE_PRECISION_TOLERANCE
          ) {
            updatedStations.push({ id: numericId, ...currentStation });
          }
        } else if (featureType === "route") {
          const originalRoute = this.routes.find((r) => r.id === numericId);
          if (!originalRoute) continue;

          const currentRoute = {
            route_number: properties.name || "",
            path: geometry.coordinates,
          };

          if (
            originalRoute.route_number !== currentRoute.route_number ||
            !arePathsEqual(originalRoute.path, currentRoute.path)
          ) {
            updatedRoutes.push({ id: numericId, ...currentRoute });
          }
        } else if (featureType === "sight") {
          const originalSight = this.sights.find((s) => s.id === numericId);
          if (!originalSight) continue;

          const currentSight = {
            name: properties.name || "",
            description: properties.description || "",
            latitude: geometry.coordinates[1],
            longitude: geometry.coordinates[0],
          };

          if (
            originalSight.name !== currentSight.name ||
            originalSight.description !== currentSight.description ||
            Math.abs(originalSight.latitude - currentSight.latitude) >
              COORDINATE_PRECISION_TOLERANCE ||
            Math.abs(originalSight.longitude - currentSight.longitude) >
              COORDINATE_PRECISION_TOLERANCE
          ) {
            updatedSights.push({ id: numericId, ...currentSight });
          }
        }
      } else {
        if (properties.featureType === "station") {
          newStations.push({
            name: properties.name || "",
            latitude: geometry.coordinates[1],
            longitude: geometry.coordinates[0],
          });
        } else if (properties.featureType === "route") {
          newRoutes.push({
            route_number: properties.name || "",
            path: geometry.coordinates,
          });
        } else if (properties.featureType === "sight") {
          newSights.push({
            name: properties.name || "",
            description: properties.description || "",
            latitude: geometry.coordinates[1],
            longitude: geometry.coordinates[0],
          });
        }
      }
    }

    const requests: Promise<any>[] = [];

    newStations.forEach((data) =>
      requests.push(languageInstance("ru").post("/station", data))
    );
    newRoutes.forEach((data) =>
      requests.push(languageInstance("ru").post("/route", data))
    );
    newSights.forEach((data) =>
      requests.push(languageInstance("ru").post("/sight", data))
    );

    updatedStations.forEach(({ id, ...data }) =>
      requests.push(languageInstance("ru").patch(`/station/${id}`, data))
    );
    updatedRoutes.forEach(({ id, ...data }) =>
      requests.push(languageInstance("ru").patch(`/route/${id}`, data))
    );
    updatedSights.forEach(({ id, ...data }) =>
      requests.push(languageInstance("ru").patch(`/sight/${id}`, data))
    );

    if (requests.length === 0) {
      return;
    }

    try {
      await Promise.all(requests);

      await Promise.all([
        this.getRoutes(),
        this.getStations(),
        this.getSights(),
      ]);
    } catch (error) {
      console.error("Ошибка при сохранении данных:", error);
      throw error;
    }
  };
}

export default new MapStore();
