/**
 * @ai-summary Business logic for community-submitted gas stations
 * @ai-context Service layer handling approval workflow, filtering, and caching
 */

import { CommunityStationsRepository } from '../data/community-stations.repository';
import {
  CommunityStation,
  SubmitCommunityStationBody,
  CommunityStationFilters,
  CommunityStationListResult,
  NearbyStationParams,
  StationBounds,
  RemovalReportResult
} from './community-stations.types';
import { redis } from '../../../core/config/redis';
import { logger } from '../../../core/logging/logger';

export class CommunityStationsService {
  private readonly CACHE_TTL = 300; // 5 minutes
  private readonly CACHE_KEY_PREFIX = 'mvp:community-stations';

  constructor(private repository: CommunityStationsRepository) {}

  async submitStation(userId: string, data: SubmitCommunityStationBody): Promise<CommunityStation> {
    logger.info('User submitting community station', { userId, name: data.name });

    // Check if station already exists at this location (within ~11 meters)
    const existingStation = await this.repository.findStationByLocation(
      data.latitude,
      data.longitude,
      0.0001 // ~11 meters tolerance
    );

    if (existingStation) {
      // Station exists - update octane flags instead of creating duplicate
      logger.info('Updating existing community station octane flags', {
        stationId: existingStation.id,
        userId,
        newHas93Octane: data.has93Octane,
        newHas93OctaneEthanolFree: data.has93OctaneEthanolFree
      });

      const updated = await this.repository.updateStationOctaneFlags(
        existingStation.id,
        data.has93Octane ?? true,
        data.has93OctaneEthanolFree ?? false
      );

      // Invalidate caches
      await this.invalidateCache('approved');
      await this.invalidateCache('nearby');
      await this.invalidateCache('bounds');

      return updated;
    }

    // No existing station - create new (auto-approved)
    const station = await this.repository.submitStation(userId, data);

    // Invalidate approved and nearby caches since stations are now auto-approved
    await this.invalidateCache('approved');
    await this.invalidateCache('nearby');
    await this.invalidateCache('bounds');

    return station;
  }

  async getMySubmissions(userId: string, limit: number = 100, offset: number = 0): Promise<CommunityStationListResult> {
    logger.info('Fetching user submissions', { userId, limit, offset });

    return this.repository.getUserSubmissions(userId, limit, offset);
  }

  async withdrawSubmission(userId: string, stationId: string): Promise<void> {
    logger.info('User withdrawing submission', { userId, stationId });

    // Verify ownership
    const station = await this.repository.getStationById(stationId);
    if (!station) {
      throw new Error('Station not found');
    }

    if (station.submittedBy !== userId) {
      throw new Error('Unauthorized: You can only withdraw your own submissions');
    }

    // Can only withdraw pending submissions
    if (station.status !== 'pending') {
      throw new Error('Can only withdraw pending submissions');
    }

    await this.repository.deleteStation(stationId);

    // Invalidate caches
    await this.invalidateCache('pending');
  }

  async getApprovedStations(limit: number = 100, offset: number = 0): Promise<CommunityStationListResult> {
    logger.info('Fetching approved stations', { limit, offset });

    const cacheKey = `${this.CACHE_KEY_PREFIX}:approved:${limit}:${offset}`;
    const cached = await redis.get(cacheKey);

    if (cached) {
      return JSON.parse(cached);
    }

    const result = await this.repository.getApprovedStations(limit, offset);

    // Cache the result
    await redis.setex(cacheKey, this.CACHE_TTL, JSON.stringify(result));

    return result;
  }

  async getApprovedNearby(params: NearbyStationParams): Promise<CommunityStation[]> {
    const { latitude, longitude, radiusKm = 50 } = params;

    logger.info('Fetching nearby approved stations', { latitude, longitude, radiusKm });

    const cacheKey = `${this.CACHE_KEY_PREFIX}:nearby:${latitude}:${longitude}:${radiusKm}`;
    const cached = await redis.get(cacheKey);

    if (cached) {
      return JSON.parse(cached);
    }

    const stations = await this.repository.getNearbyApprovedStations(latitude, longitude, radiusKm);

    // Cache the result
    await redis.setex(cacheKey, this.CACHE_TTL, JSON.stringify(stations));

    return stations;
  }

  async getPendingReview(limit: number = 100, offset: number = 0): Promise<CommunityStationListResult> {
    logger.info('Fetching pending stations for review', { limit, offset });

    return this.repository.getPendingStations(limit, offset);
  }

  async getStationsForAdmin(filters: CommunityStationFilters): Promise<CommunityStationListResult> {
    logger.info('Fetching stations with admin filters', { filters });

    return this.repository.getAllStationsWithFilters(filters);
  }

  async reviewStation(
    adminId: string,
    stationId: string,
    status: 'approved' | 'rejected',
    rejectionReason?: string
  ): Promise<CommunityStation> {
    logger.info('Admin reviewing station', { adminId, stationId, status });

    // Verify rejection reason if rejecting
    if (status === 'rejected' && !rejectionReason) {
      throw new Error('Rejection reason required when rejecting a station');
    }

    const station = await this.repository.reviewStation(stationId, adminId, status, rejectionReason);

    // Invalidate related caches
    await this.invalidateCache('pending');
    if (status === 'approved') {
      await this.invalidateCache('approved');
      await this.invalidateCache('nearby');
      await this.invalidateCache('bounds');
    }

    return station;
  }

  /**
   * Submit a removal report for a station that no longer has Premium 93
   * When 2+ users report the same station, it is marked as 'removed'
   */
  async submitRemovalReport(
    userId: string,
    stationId: string,
    reason?: string
  ): Promise<RemovalReportResult> {
    logger.info('User submitting removal report', { userId, stationId });

    // Verify station exists
    const station = await this.repository.getStationById(stationId);
    if (!station) {
      throw new Error('Station not found');
    }

    // Check if station is already removed
    if (station.status === 'removed') {
      throw new Error('Station has already been removed');
    }

    // Check if user already reported this station
    const alreadyReported = await this.repository.hasUserReportedStation(userId, stationId);
    if (alreadyReported) {
      throw new Error('You have already reported this station');
    }

    const result = await this.repository.submitRemovalReport(userId, stationId, reason);

    // Invalidate caches if station was removed
    if (result.stationRemoved) {
      await this.invalidateCache('approved');
      await this.invalidateCache('nearby');
      await this.invalidateCache('bounds');
    }

    return result;
  }

  /**
   * Find a station by coordinates (for linking reports to existing stations)
   */
  async findStationByLocation(latitude: number, longitude: number): Promise<CommunityStation | null> {
    return this.repository.findStationByLocation(latitude, longitude);
  }

  /**
   * Get approved stations within map bounds
   */
  async getApprovedInBounds(bounds: StationBounds): Promise<CommunityStation[]> {
    logger.info('Fetching approved stations in bounds', { bounds });

    // Create cache key with rounded bounds for efficiency
    const roundedBounds = {
      north: Math.round(bounds.north * 100) / 100,
      south: Math.round(bounds.south * 100) / 100,
      east: Math.round(bounds.east * 100) / 100,
      west: Math.round(bounds.west * 100) / 100
    };

    const cacheKey = `${this.CACHE_KEY_PREFIX}:bounds:${roundedBounds.north}:${roundedBounds.south}:${roundedBounds.east}:${roundedBounds.west}`;
    const cached = await redis.get(cacheKey);

    if (cached) {
      return JSON.parse(cached);
    }

    const stations = await this.repository.getApprovedStationsInBounds(bounds);

    await redis.setex(cacheKey, this.CACHE_TTL, JSON.stringify(stations));

    return stations;
  }

  private async invalidateCache(type: string): Promise<void> {
    try {
      if (type === 'pending') {
        const pendingKeys = await redis.keys(`${this.CACHE_KEY_PREFIX}:pending:*`);
        if (pendingKeys.length > 0) {
          await redis.del(...pendingKeys);
        }
      } else if (type === 'approved') {
        const approvedKeys = await redis.keys(`${this.CACHE_KEY_PREFIX}:approved:*`);
        if (approvedKeys.length > 0) {
          await redis.del(...approvedKeys);
        }
      } else if (type === 'nearby') {
        const nearbyKeys = await redis.keys(`${this.CACHE_KEY_PREFIX}:nearby:*`);
        if (nearbyKeys.length > 0) {
          await redis.del(...nearbyKeys);
        }
      } else if (type === 'bounds') {
        const boundsKeys = await redis.keys(`${this.CACHE_KEY_PREFIX}:bounds:*`);
        if (boundsKeys.length > 0) {
          await redis.del(...boundsKeys);
        }
      }
    } catch (error) {
      logger.error('Error invalidating cache', { error, type });
      // Don't throw - cache invalidation failure shouldn't fail the operation
    }
  }
}
