import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from "@nestjs/common"
import { Request } from "express"
import { Observable } from "rxjs"
import { failureResponse } from "../common/response/response"
import { messageKey } from "../constants/message-keys"
import { CustomersService } from "../modules/customers/v1/customers.service"
import { getCurrentEnvironment, validationMessage } from "../utils/helpers"
import moment from "moment"

interface AuthenticatedRequest extends Request {
  customer?: any
}

@Injectable()
export class CustomerAuthGuardMiddleware implements CanActivate {
  constructor(private readonly customersService: CustomersService) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const req = context.switchToHttp().getRequest<AuthenticatedRequest>()
    return this.validateRequest(req)
  }

  private async validateRequest(req: AuthenticatedRequest): Promise<boolean> {
    // Allow bypass in local environment
    if (getCurrentEnvironment() === "local") return true

    // Extract token from headers
    const token = req.headers["authorization"]?.replace("Bearer ", "")

    if (!token) {
      throw new UnauthorizedException(
        failureResponse(401, validationMessage(messageKey.invalid_token)),
      )
    }

    // Use service function to find customer by token
    const customerSession =
      await this.customersService.getCustomerByToken(token)

    if (!customerSession) {
      throw new UnauthorizedException(
        failureResponse(401, validationMessage(messageKey.invalid_token)),
      )
    }

    // Skip token expiration check for Android and iOS devices
    // Mobile apps should only logout when user explicitly logs out
    const deviceType = customerSession.device_type?.toLowerCase()
    const isMobileDevice = deviceType === "android" || deviceType === "ios"

    if (!isMobileDevice) {
      // Only check expiration for web and other devices
      const now = moment().utc(true)
      const expiry = moment(customerSession.access_token_expire_at).utc(true)

      if (now.isSameOrAfter(expiry)) {
        await this.customersService.customerLogout(token)
        throw new UnauthorizedException(
          failureResponse(401, validationMessage(messageKey.token_expire)),
        )
      }
    }

    // Attach authenticated customer to request
    req.customer = customerSession

    return true
  }
}
