import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  ConnectedSocket,
  MessageBody,
  OnGatewayConnection,
} from "@nestjs/websockets"
import { Server, Socket } from "socket.io"
import { TripsService } from "./v1/trips.service"
import { CreateTripTrackingDto } from "./dto/trip-tracking.dto"
import { AuthService } from "../auth/v1/auth.service"
import moment from "moment"
import { CustomersService } from "../customers/v1/customers.service"

@WebSocketGateway({
  cors: { origin: "*" },
  pingInterval: 25000,
  pingTimeout: 90000,
})
export class TripTrackingGateway implements OnGatewayConnection {
  @WebSocketServer()
  server: Server

  constructor(
    private readonly tripService: TripsService,
    private readonly authService: AuthService,
    private readonly customerService: CustomersService,
  ) {}

  async handleConnection(client: Socket) {
    try {
      const token =
        client.handshake.auth?.token ||
        client.handshake.headers?.authorization?.replace("Bearer ", "")

      const isCustomer = client.handshake.auth?.isCustomer

      if (!token) {
        console.log("Socket disconnected: No token provided")
        client.disconnect()
        return
      }

      // Get user by token
      if (isCustomer) {
        const loggedInCustomer =
          await this.customerService.getCustomerByToken(token)
        if (!loggedInCustomer) {
          console.log("Socket disconnected: Customer not found")
          client.disconnect()
          return
        }

        // Skip token expiration check for Android and iOS devices
        const deviceTypeCustomer = loggedInCustomer.device_type?.toLowerCase()
        const isMobileDeviceCustomer =
          deviceTypeCustomer === "android" || deviceTypeCustomer === "ios"

        if (!isMobileDeviceCustomer) {
          const expirationTimeCustomer = moment(
            loggedInCustomer.access_token_expire_at,
          ).utc(true)
          const currentTime = moment().utc(true)

          if (currentTime >= expirationTimeCustomer) {
            console.log("Socket disconnected: Customer token expired")
            client.disconnect()
            return
          }
        }

        ;(client as any).customer = loggedInCustomer

        console.log("Socket connected:", loggedInCustomer.customer_id)
      } else {
        const loggedInUser = await this.authService.getUserByToken(token)
        if (!loggedInUser) {
          console.log("Socket disconnected: User not found")
          client.disconnect()
          return
        }

        // Skip token expiration check for Android and iOS devices
        const deviceType = loggedInUser.device_type?.toLowerCase()
        const isMobileDevice = deviceType === "android" || deviceType === "ios"

        if (!isMobileDevice) {
          const expirationTime = moment(
            loggedInUser.access_token_expire_at,
          ).utc(true)
          const currentTime = moment().utc(true)

          if (currentTime >= expirationTime) {
            await this.authService.logout(token)
            console.log("Socket disconnected: User token expired")
            client.disconnect()
            return
          }
        }

        ;(client as any).user = loggedInUser
        console.log("Socket connected:", loggedInUser.user_id)
      }
    } catch (error) {
      console.error("Socket authentication error:", error)
      client.disconnect()
    }
  }

  @SubscribeMessage("updateLocation")
  async handleUpdateLocation(@MessageBody() data: CreateTripTrackingDto) {
    const parsedData = typeof data === "string" ? JSON.parse(data) : data

    const location = await this.tripService.saveTripLocation(parsedData)
    this.server.to(`trip-${data.trip_id}`).emit("locationUpdate", location)
    return { status: "ok", location }
  }

  @SubscribeMessage("joinTrip")
  async handleJoinTrip(
    @MessageBody() tripId: number,
    @ConnectedSocket() client: Socket,
  ) {
    client.join(`trip-${tripId}`)
    const latest = await this.tripService.getLatestTripLocation(tripId)
    this.server.to(`trip-${tripId}`).emit("locationUpdate", latest)
    return { status: "ok", message: `Joined room trip-${tripId}` }
  }

  @SubscribeMessage("getLatestLocation")
  async handleGetLatestLocation(@MessageBody() tripId: number) {
    console.log(`Fetching latest location for trip ${tripId}`)
    const latest = await this.tripService.getLatestTripLocation(tripId)
    // Send back directly to requester
    return { status: "ok", latest }
  }
}
