/**
 * @ai-summary Integration tests for Stations API endpoints
 */

import { FastifyInstance } from 'fastify';
import { buildApp } from '../../../../app';
import { pool } from '../../../../core/config/database';
import {
  mockStations,
  mockUserId,
  searchCoordinates
} from '../fixtures/mock-stations';
import { googleMapsClient } from '../../external/google-maps/google-maps.client';

jest.mock('../../external/google-maps/google-maps.client');

describe('Stations API Integration Tests', () => {
  let app: FastifyInstance;
  const mockToken = 'test-jwt-token';
  const authHeader = { authorization: `Bearer ${mockToken}` };

  beforeAll(async () => {
    app = await buildApp();
    await app.ready();
  });

  afterAll(async () => {
    await app.close();
  });

  beforeEach(async () => {
    jest.clearAllMocks();
    // Clean up test data
    await pool.query('DELETE FROM saved_stations WHERE user_id = $1', [mockUserId]);
    await pool.query('DELETE FROM station_cache');
  });

  describe('POST /api/stations/search', () => {
    it('should search for nearby stations', async () => {
      (googleMapsClient.searchNearbyStations as jest.Mock).mockResolvedValue(mockStations);

      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/search',
        headers: authHeader,
        payload: {
          latitude: searchCoordinates.sanFrancisco.latitude,
          longitude: searchCoordinates.sanFrancisco.longitude,
          radius: 5000
        }
      });

      expect(response.statusCode).toBe(200);
      const body = JSON.parse(response.body);
      expect(body.stations).toBeDefined();
      expect(body.searchLocation).toBeDefined();
      expect(body.searchRadius).toBe(5000);
    });

    it('should return 400 for missing coordinates', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/search',
        headers: authHeader,
        payload: {
          radius: 5000
        }
      });

      expect(response.statusCode).toBe(400);
      const body = JSON.parse(response.body);
      expect(body.message).toContain('required');
    });

    it('should return 401 without authentication', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/search',
        payload: {
          latitude: searchCoordinates.sanFrancisco.latitude,
          longitude: searchCoordinates.sanFrancisco.longitude
        }
      });

      expect(response.statusCode).toBe(401);
    });

    it('should validate coordinate ranges', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/search',
        headers: authHeader,
        payload: {
          latitude: 91, // Invalid: max is 90
          longitude: searchCoordinates.sanFrancisco.longitude
        }
      });

      expect(response.statusCode).toBe(400);
    });
  });

  describe('POST /api/stations/save', () => {
    beforeEach(async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      // Cache a station first
      await pool.query(
        `INSERT INTO station_cache (place_id, name, address, latitude, longitude, rating)
         VALUES ($1, $2, $3, $4, $5, $6)`,
        [
          station.placeId,
          station.name,
          station.address,
          station.latitude,
          station.longitude,
          station.rating
        ]
      );
    });

    it('should save a station to user favorites', async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/save',
        headers: authHeader,
        payload: {
          placeId: station.placeId,
          nickname: 'Work Gas Station'
        }
      });

      expect(response.statusCode).toBe(201);
      const body = JSON.parse(response.body);
      expect(body.station).toBeDefined();
    });

    it('should require valid placeId', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/save',
        headers: authHeader,
        payload: {
          placeId: ''
        }
      });

      expect(response.statusCode).toBe(400);
    });

    it('should handle station not in cache', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/save',
        headers: authHeader,
        payload: {
          placeId: 'non-existent-place-id'
        }
      });

      expect(response.statusCode).toBe(404);
    });

    it('should verify user isolation', async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      // Save for one user
      await app.inject({
        method: 'POST',
        url: '/api/stations/save',
        headers: authHeader,
        payload: {
          placeId: station.placeId
        }
      });

      // Verify another user can't see it
      const otherUserHeaders = { authorization: 'Bearer other-user-token' };
      const response = await app.inject({
        method: 'GET',
        url: '/api/stations/saved',
        headers: otherUserHeaders
      });

      const body = JSON.parse(response.body);
      expect(body).toEqual([]);
    });
  });

  describe('GET /api/stations/saved', () => {
    beforeEach(async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      // Insert test data
      await pool.query(
        `INSERT INTO station_cache (place_id, name, address, latitude, longitude)
         VALUES ($1, $2, $3, $4, $5)`,
        [
          station.placeId,
          station.name,
          station.address,
          station.latitude,
          station.longitude
        ]
      );

      await pool.query(
        `INSERT INTO saved_stations (user_id, place_id, nickname, notes, is_favorite)
         VALUES ($1, $2, $3, $4, $5)`,
        [
          mockUserId,
          station.placeId,
          'Test Station',
          'Test notes',
          true
        ]
      );
    });

    it('should return user saved stations', async () => {
      const response = await app.inject({
        method: 'GET',
        url: '/api/stations/saved',
        headers: authHeader
      });

      expect(response.statusCode).toBe(200);
      const body = JSON.parse(response.body);
      expect(Array.isArray(body)).toBe(true);
      expect(body.length).toBeGreaterThan(0);
    });

    it('should only return current user stations', async () => {
      const response = await app.inject({
        method: 'GET',
        url: '/api/stations/saved',
        headers: authHeader
      });

      const body = JSON.parse(response.body);
      body.forEach((station: any) => {
        expect(station.userId).toBe(mockUserId);
      });
    });

    it('should return empty array for user with no saved stations', async () => {
      const otherUserHeaders = { authorization: 'Bearer other-user-token' };
      const response = await app.inject({
        method: 'GET',
        url: '/api/stations/saved',
        headers: otherUserHeaders
      });

      expect(response.statusCode).toBe(200);
      const body = JSON.parse(response.body);
      expect(body).toEqual([]);
    });

    it('should include station metadata', async () => {
      const response = await app.inject({
        method: 'GET',
        url: '/api/stations/saved',
        headers: authHeader
      });

      const body = JSON.parse(response.body);
      const station = body[0];
      expect(station).toHaveProperty('id');
      expect(station).toHaveProperty('nickname');
      expect(station).toHaveProperty('notes');
      expect(station).toHaveProperty('isFavorite');
    });
  });

  describe('DELETE /api/stations/saved/:placeId', () => {
    beforeEach(async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      await pool.query(
        `INSERT INTO station_cache (place_id, name, address, latitude, longitude)
         VALUES ($1, $2, $3, $4, $5)`,
        [
          station.placeId,
          station.name,
          station.address,
          station.latitude,
          station.longitude
        ]
      );

      await pool.query(
        `INSERT INTO saved_stations (user_id, place_id, nickname)
         VALUES ($1, $2, $3)`,
        [mockUserId, station.placeId, 'Test Station']
      );
    });

    it('should delete a saved station', async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      const response = await app.inject({
        method: 'DELETE',
        url: `/api/stations/saved/${station.placeId}`,
        headers: authHeader
      });

      expect(response.statusCode).toBe(204);
    });

    it('should return 404 if station not found', async () => {
      const response = await app.inject({
        method: 'DELETE',
        url: '/api/stations/saved/non-existent-id',
        headers: authHeader
      });

      expect(response.statusCode).toBe(404);
    });

    it('should verify ownership before deleting', async () => {
      const station = mockStations[0];
      if (!station) throw new Error('Mock station not found');

      const otherUserHeaders = { authorization: 'Bearer other-user-token' };
      const response = await app.inject({
        method: 'DELETE',
        url: `/api/stations/saved/${station.placeId}`,
        headers: otherUserHeaders
      });

      expect(response.statusCode).toBe(404);
    });
  });

  describe('Error Handling', () => {
    it('should handle Google Maps API errors gracefully', async () => {
      (googleMapsClient.searchNearbyStations as jest.Mock).mockRejectedValue(
        new Error('API Error')
      );

      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/search',
        headers: authHeader,
        payload: {
          latitude: searchCoordinates.sanFrancisco.latitude,
          longitude: searchCoordinates.sanFrancisco.longitude
        }
      });

      expect(response.statusCode).toBe(500);
      const body = JSON.parse(response.body);
      expect(body.error).toBeDefined();
    });

    it('should validate request schema', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/stations/search',
        headers: authHeader,
        payload: {
          invalidField: 'test'
        }
      });

      expect(response.statusCode).toBe(400);
    });

    it('should require authentication', async () => {
      const response = await app.inject({
        method: 'GET',
        url: '/api/stations/saved'
      });

      expect(response.statusCode).toBe(401);
    });
  });
});
