import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { lastValueFrom, Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { CityAndStateResult, CountyResult, GeoResult, IpInfo } from '../../../../bizmatch-server/src/models/main.model';
import { Place } from '../../../../bizmatch-server/src/models/server.model';
import { environment } from '../../environments/environment';

interface CachedBoundary {
  data: any;
  timestamp: number;
}

@Injectable({
  providedIn: 'root',
})
export class GeoService {
  private apiBaseUrl = environment.apiBaseUrl;
  private baseUrl: string = 'https://nominatim.openstreetmap.org/search';
  private fetchingData: Observable<IpInfo> | null = null;
  private readonly storageKey = 'ipInfo';
  private readonly boundaryStoragePrefix = 'nominatim_boundary_';
  private readonly cacheExpiration = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
  private platformId = inject(PLATFORM_ID);
  private isBrowser = isPlatformBrowser(this.platformId);

  constructor(private http: HttpClient) {}

  /**
   * Get cached boundary data from localStorage
   */
  private getCachedBoundary(cacheKey: string): any | null {
    if (!this.isBrowser) return null;

    try {
      const cached = localStorage.getItem(this.boundaryStoragePrefix + cacheKey);
      if (!cached) {
        return null;
      }

      const cachedData: CachedBoundary = JSON.parse(cached);
      const now = Date.now();

      // Check if cache has expired
      if (now - cachedData.timestamp > this.cacheExpiration) {
        localStorage.removeItem(this.boundaryStoragePrefix + cacheKey);
        return null;
      }

      return cachedData.data;
    } catch (error) {
      console.error('Error reading boundary cache:', error);
      return null;
    }
  }

  /**
   * Save boundary data to localStorage
   */
  private setCachedBoundary(cacheKey: string, data: any): void {
    if (!this.isBrowser) return;

    try {
      const cachedData: CachedBoundary = {
        data: data,
        timestamp: Date.now()
      };
      localStorage.setItem(this.boundaryStoragePrefix + cacheKey, JSON.stringify(cachedData));
    } catch (error) {
      console.error('Error saving boundary cache:', error);
    }
  }

  /**
   * Clear all cached boundary data
   */
  clearBoundaryCache(): void {
    if (!this.isBrowser) return;

    try {
      const keys = Object.keys(localStorage);
      keys.forEach(key => {
        if (key.startsWith(this.boundaryStoragePrefix)) {
          localStorage.removeItem(key);
        }
      });
    } catch (error) {
      console.error('Error clearing boundary cache:', error);
    }
  }

  findCitiesStartingWith(prefix: string, state?: string): Observable<GeoResult[]> {
    const stateString = state ? `/${state}` : '';
    return this.http.get<GeoResult[]>(`${this.apiBaseUrl}/bizmatch/geo/${prefix}${stateString}`);
  }
  findCitiesAndStatesStartingWith(prefix: string): Observable<CityAndStateResult[]> {
    return this.http.get<CityAndStateResult[]>(`${this.apiBaseUrl}/bizmatch/geo/citiesandstates/${prefix}`);
  }
  findCountiesStartingWith(prefix: string, states?: string[]): Observable<CountyResult[]> {
    return this.http.post<CountyResult[]>(`${this.apiBaseUrl}/bizmatch/geo/counties`, { prefix, states });
  }
  findLocationStartingWith(prefix: string): Observable<Place[]> {
    let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
    return this.http.get(`${this.baseUrl}?q=${prefix},US&format=json&addressdetails=1&limit=5`, { headers }) as Observable<Place[]>;
  }

  getCityBoundary(cityName: string, state: string): Observable<any> {
    const cacheKey = `city_${cityName}_${state}`.toLowerCase().replace(/\s+/g, '_');

    // Check cache first
    const cached = this.getCachedBoundary(cacheKey);
    if (cached) {
      return of(cached);
    }

    // If not in cache, fetch from API
    const query = `${cityName}, ${state}, USA`;
    let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
    return this.http.get(`${this.baseUrl}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=1`, { headers }).pipe(
      tap(data => this.setCachedBoundary(cacheKey, data))
    );
  }

  getStateBoundary(state: string): Observable<any> {
    const cacheKey = `state_${state}`.toLowerCase().replace(/\s+/g, '_');

    // Check cache first
    const cached = this.getCachedBoundary(cacheKey);
    if (cached) {
      return of(cached);
    }

    // If not in cache, fetch from API
    const query = `${state}, USA`;
    let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
    return this.http.get(`${this.baseUrl}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=1&featuretype=state`, { headers }).pipe(
      tap(data => this.setCachedBoundary(cacheKey, data))
    );
  }

  private fetchIpAndGeoLocation(): Observable<IpInfo> {
    return this.http.get<IpInfo>(`${this.apiBaseUrl}/bizmatch/geo/ipinfo/georesult/wysiwyg`);
  }

  async getIpInfo(): Promise<IpInfo | null> {
    // Versuche zuerst, die Daten aus dem sessionStorage zu holen
    if (this.isBrowser) {
      const storedData = sessionStorage.getItem(this.storageKey);
      if (storedData) {
        return JSON.parse(storedData);
      }
    }

    try {
      // Wenn keine Daten im Storage, hole sie vom Server
      const data = await lastValueFrom(this.http.get<IpInfo>(`${this.apiBaseUrl}/bizmatch/geo/ipinfo/georesult/wysiwyg`));

      // Speichere die Daten im sessionStorage
      if (this.isBrowser) {
        sessionStorage.setItem(this.storageKey, JSON.stringify(data));
      }

      return data;
    } catch (error) {
      console.error('Error fetching IP info:', error);
      return null;
    }
  }
}
