import type { MapContainer } from '@/components/MapContainer';
import type { MapLayers } from '@/types';
import { fetchEarthquakes } from '@/services/earthquakes';
import { fetchNaturalEvents } from '@/services/eonet';
import { fetchProtestEvents } from '@/services/unrest';
import { fetchWeatherAlerts } from '@/services/weather';

import { startSmartPollLoop, type SmartPollLoopHandle } from '@/services/smart-poll-loop';
import type { EmbedLayerId } from './embed-url';
import { ConflictServiceClient } from '@/services/generated-rpc-clients';

const REFRESH_MS = 10 * 60 * 1000;
const CONFLICT_WINDOW_MS = 30 * 24 * 60 * 60 * 1000;
const conflictClient = new ConflictServiceClient('', { fetch: (...args) => globalThis.fetch(...args) });
const STATIC_LAYER_READY_BY_EMBED_ID: Partial<Record<EmbedLayerId, keyof MapLayers>> = {
  cables: 'cables',
  pipelines: 'pipelines',
  waterways: 'waterways',
  tradeRoutes: 'tradeRoutes',
  economic: 'economic',
  stockExchanges: 'stockExchanges',
  financialCenters: 'financialCenters',
  centralBanks: 'centralBanks',
  commodityHubs: 'commodityHubs',
  gulfInvestments: 'gulfInvestments',
};

export class EmbedDataLoader {
  private refreshLoop: SmartPollLoopHandle | null = null;

  constructor(
    private readonly map: MapContainer,
    private readonly activeLayerIds: readonly EmbedLayerId[],
  ) {}

  async start(): Promise<void> {
    await this.loadOnce();
    this.refreshLoop = startSmartPollLoop(() => this.loadOnce(), {
      intervalMs: REFRESH_MS,
      pauseWhenHidden: true,
      refreshOnVisible: true,
      runImmediately: false,
    });
  }

  destroy(): void {
    if (this.refreshLoop !== null) {
      this.refreshLoop.stop();
      this.refreshLoop = null;
    }
  }

  async loadOnce(): Promise<void> {
    await Promise.all(this.activeLayerIds.map((id) => this.loadLayer(id)));
  }

  private async loadLayer(id: EmbedLayerId): Promise<void> {
    switch (id) {
      case 'conflicts':
        await this.loadConflicts();
        return;
      case 'earthquakes':
        await this.loadEarthquakes();
        return;
      case 'protests':
        await this.loadProtests();
        return;
      case 'weather':
        await this.loadWeather();
        return;
      default:
        this.markStaticLayerReady(id);
        return;
    }
  }

  private markStaticLayerReady(id: EmbedLayerId): void {
    const layer = STATIC_LAYER_READY_BY_EMBED_ID[id];
    if (layer) this.map.setLayerReady(layer, true);
  }

  private async loadConflicts(): Promise<void> {
    if (!this.map.supportsLiveConflictEvents()) {
      this.map.setLayerReady('conflicts', true);
      return;
    }

    await this.withLayerState('conflicts', async () => {
      const end = Date.now();
      const start = end - CONFLICT_WINDOW_MS;
      const data = await conflictClient.listAcledEvents({ country: '', start, end, pageSize: 0, cursor: '' });
      this.map.setConflictEvents(data.events);
      return data.events.length > 0;
    });
  }

  private async loadEarthquakes(): Promise<void> {
    await this.withLayerState('natural', async () => {
      const [earthquakesResult, naturalEventsResult] = await Promise.allSettled([
        fetchEarthquakes(),
        fetchNaturalEvents(30),
      ]);
      if (earthquakesResult.status === 'fulfilled') {
        this.map.setEarthquakes(earthquakesResult.value);
      }
      if (naturalEventsResult.status === 'fulfilled') {
        this.map.setNaturalEvents(naturalEventsResult.value);
      }
      return earthquakesResult.status === 'fulfilled' || naturalEventsResult.status === 'fulfilled';
    });
  }

  private async loadProtests(): Promise<void> {
    await this.withLayerState('protests', async () => {
      const data = await fetchProtestEvents();
      this.map.setProtests(data.events);
      return true;
    });
  }

  private async loadWeather(): Promise<void> {
    await this.withLayerState('weather', async () => {
      const alerts = await fetchWeatherAlerts();
      this.map.setWeatherAlerts(alerts);
      return true;
    });
  }

  private async withLayerState(layer: keyof MapLayers, load: () => Promise<boolean>): Promise<void> {
    this.map.setLayerLoading(layer, true);
    try {
      const hasData = await load();
      this.map.setLayerReady(layer, hasData);
    } catch (error) {
      console.warn(`[embed] Failed to load ${layer}:`, error);
      this.map.setLayerReady(layer, false);
    } finally {
      this.map.setLayerLoading(layer, false);
    }
  }
}
