import {
  BadRequestException,
  Body,
  Controller,
  Delete,
  Get,
  Patch,
  Param,
  Post,
  Query,
  Req,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { UsersService } from './users.service';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { RolesGuard } from '../../common/guards/roles.guard';
import { Roles } from '../../common/decorators/roles.decorator';
import { UserRole } from '@prisma/client';
import { CreateAdminWithShopDto } from './dto/create-admin-with-shop.dto';
import { Request } from 'express';
import { CreateSubadminDto } from './dto/create-subadmin.dto';
import { CreateCustomerDto } from './dto/create-customer.dto';
import { SetShopActiveDto } from './dto/set-shop-active.dto';
import { CreateOwnedShopDto } from './dto/create-owned-shop.dto';
import { SetShopPlanDto } from './dto/set-shop-plan.dto';
import { UpdateOwnedShopDto } from './dto/update-owned-shop.dto';
import { UpdateSubadminShopsDto } from './dto/update-subadmin-shops.dto';
import { UpdateSubadminPermissionsDto } from './dto/update-subadmin-permissions.dto';
import { UpdateShopIntegrationsDto } from './dto/update-shop-integrations.dto';
import { RejectShopDto } from './dto/reject-shop.dto';
import { SetShopStatusDto } from './dto/set-shop-status.dto';
import { SetCustomerActiveDto } from './dto/set-customer-active.dto';
import { CreateSuperadminDto } from './dto/create-superadmin.dto';
import { SetUserActiveDto } from './dto/set-user-active.dto';
import { UpdateManagedUserDto } from './dto/update-managed-user.dto';
import { UpdateMyLocationDto } from './dto/update-my-location.dto';
import { UpdateMyProfileDto } from './dto/update-my-profile.dto';
import { ClaimCouponDto } from './dto/claim-coupon.dto';
import { FileInterceptor } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';

type AuthedRequest = Request & {
  user?: { id: string; role: UserRole; shopId?: string | null };
};

@ApiTags('users')
@Controller('users')
export class UsersController {
  constructor(private readonly users: UsersService) {}

  @Get('me/dashboard')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Dashboard summary for logged-in customer' })
  myDashboard(@Req() req: AuthedRequest) {
    return this.users.getMyDashboard(req.user!.id);
  }

  @Get('me/earnings')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Recent earnings/redeems history for logged-in customer',
  })
  myEarnings(
    @Req() req: AuthedRequest,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
  ) {
    const p = page ? Number(page) : 1;
    const l = limit ? Number(limit) : 20;
    const pageNum = Number.isFinite(p) ? Math.max(Math.floor(p), 1) : 1;
    const limitNum = Number.isFinite(l)
      ? Math.min(Math.max(Math.floor(l), 1), 100)
      : 20;
    return this.users.listMyEarnings(req.user!.id, pageNum, limitNum);
  }

  @Get('me/shop-activity')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'Shop activity summary + transaction history for logged-in customer',
  })
  myShopActivity(
    @Req() req: AuthedRequest,
    @Query('shopId') shopId?: string,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
  ) {
    const sid = String(shopId ?? '').trim();
    if (!sid) throw new BadRequestException('shopId is required');

    const p = page ? Number(page) : 1;
    const l = limit ? Number(limit) : 20;
    const pageNum = Number.isFinite(p) ? Math.max(Math.floor(p), 1) : 1;
    const limitNum = Number.isFinite(l)
      ? Math.min(Math.max(Math.floor(l), 1), 100)
      : 20;
    return this.users.getMyShopActivity(req.user!.id, sid, pageNum, limitNum);
  }

  @Get('me/coupons')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'List coupons for the logged-in customer (active/used/expired)',
  })
  myCoupons(@Req() req: AuthedRequest) {
    return this.users.listMyCoupons(req.user!.id);
  }

  @Get('me/rewards/available-coupons')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'List nearby coupons that are ACTIVE+PUBLISHED and not yet claimed/redeemed by the logged-in customer',
  })
  myAvailableCoupons(
    @Req() req: AuthedRequest,
    @Query('shopId') shopId?: string,
    @Query('lat') lat?: string,
    @Query('lng') lng?: string,
    @Query('radiusKm') radiusKm?: string,
    @Query('limit') limit?: string,
  ) {
    const sid = String(shopId ?? '').trim();
    const latNum = lat != null && lat !== '' ? Number(lat) : null;
    const lngNum = lng != null && lng !== '' ? Number(lng) : null;
    const r = radiusKm != null && radiusKm !== '' ? Number(radiusKm) : null;
    const l = limit != null && limit !== '' ? Number(limit) : null;
    const radius =
      r != null && Number.isFinite(r) ? Math.min(Math.max(r, 1), 100) : 10;
    const take =
      l != null && Number.isFinite(l)
        ? Math.min(Math.max(Math.floor(l), 1), 100)
        : 30;
    const coords =
      latNum != null &&
      Number.isFinite(latNum) &&
      lngNum != null &&
      Number.isFinite(lngNum)
        ? { lat: latNum, lng: lngNum }
        : null;

    return this.users.listMyAvailableCoupons(req.user!.id, {
      shopId: sid || undefined,
      coords: coords ?? undefined,
      radiusKm: radius,
      limit: take,
    });
  }

  @Post('me/rewards/claim-coupon')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Claim a coupon using Cashi points (creates assignment)',
  })
  claimCoupon(@Req() req: AuthedRequest, @Body() dto: ClaimCouponDto) {
    return this.users.claimCouponWithPoints(req.user!.id, dto);
  }

  @Patch('me/profile')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Update the logged-in customer name / email' })
  updateMyProfile(@Req() req: AuthedRequest, @Body() dto: UpdateMyProfileDto) {
    return this.users.updateMyProfile(req.user!.id, dto);
  }

  @Patch('me/location')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Store last known coordinates for nearby features',
  })
  updateMyLocation(
    @Req() req: AuthedRequest,
    @Body() dto: UpdateMyLocationDto,
  ) {
    return this.users.updateMyLocation(req.user!.id, dto);
  }

  @Post('me/deactivate')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Deactivate your account (soft delete). Tokens are revoked.',
  })
  deactivateMe(@Req() req: AuthedRequest) {
    return this.users.deactivateMe(req.user!.id);
  }

  @Get('shops')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUPERADMIN, UserRole.SUBADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'Lists shops. If page/limit are provided, returns paginated result. (ADMIN: owned shops, SUPERADMIN: all shops, SUBADMIN: assigned shops)',
  })
  listOwnedShops(
    @Req() req: AuthedRequest,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('q') q?: string,
    @Query('status') status?: string,
    @Query('isActive') isActive?: string,
    @Query('planCode') planCode?: string,
  ) {
    const p = page != null ? Number(page) : null;
    const l = limit != null ? Number(limit) : null;
    const wantsPaged =
      (p != null && Number.isFinite(p)) || (l != null && Number.isFinite(l));

    if (!wantsPaged) return this.users.listShopsForRequester(req.user!);

    const pageNum =
      p != null && Number.isFinite(p) ? Math.max(1, Math.floor(p)) : 1;
    const limitNum =
      l != null && Number.isFinite(l)
        ? Math.min(100, Math.max(1, Math.floor(l)))
        : 20;

    return this.users.listShopsForRequesterPaged(req.user!, {
      page: pageNum,
      limit: limitNum,
      q: (q ?? '').toString(),
      status: status?.toString(),
      isActive: isActive?.toString(),
      planCode: planCode?.toString(),
    });
  }

  @Post('shops')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Admin creates an additional shop (multi-shop)',
  })
  createShop(@Req() req: AuthedRequest, @Body() dto: CreateOwnedShopDto) {
    return this.users.createShopForAdmin(req.user!.id, dto);
  }

  @Patch('shops/:id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Updates a shop (ADMIN: owned only, SUPERADMIN: any)',
  })
  updateShop(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: UpdateOwnedShopDto,
  ) {
    return this.users.updateShopForRequester(req.user!, id, dto);
  }

  @Post('shops/:id/image')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Upload shop image (owner only)' })
  @UseInterceptors(
    FileInterceptor('file', {
      limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
      storage: memoryStorage(),
    }),
  )
  uploadShopImage(
    @Req() req: AuthedRequest & { file?: any },
    @Param('id') id: string,
  ) {
    const file = (req as any).file;
    if (!file) throw new BadRequestException('file is required');
    if (!file.mimetype?.startsWith('image/'))
      throw new BadRequestException('Only image uploads are allowed');
    return this.users.uploadShopImageForRequester(req.user!, id, file);
  }

  @Get('shops/:id/integrations')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Get integrations config for a shop (owner only)',
  })
  getShopIntegrations(@Req() req: AuthedRequest, @Param('id') id: string) {
    return this.users.getShopIntegrations(req.user!, id);
  }

  @Patch('shops/:id/integrations')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Update integrations config for a shop (owner only)',
  })
  updateShopIntegrations(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: UpdateShopIntegrationsDto,
  ) {
    return this.users.updateShopIntegrations(req.user!, id, dto);
  }

  @Post('shops/:id/switch')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUBADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Switch current shop context (ADMIN/SUBADMIN)',
  })
  switchShop(@Req() req: AuthedRequest, @Param('id') id: string) {
    return this.users.switchCurrentShop(req.user!, id);
  }

  @Post('shops/:id/plan')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Admin switches plan for a shop they own' })
  setShopPlan(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: SetShopPlanDto,
  ) {
    const origin = (req.headers?.origin ?? '').toString().trim();
    const referer = (req.headers?.referer ?? req.headers?.referrer ?? '')
      .toString()
      .trim();
    let portalPublicUrl: string | null = origin || null;
    if (!portalPublicUrl && referer) {
      try {
        portalPublicUrl = new URL(referer).origin;
      } catch {
        portalPublicUrl = null;
      }
    }

    return this.users.setShopPlanForAdmin(
      req.user!.id,
      id,
      {
        planCode: dto.planCode,
        renew: dto.renew ?? false,
      },
      portalPublicUrl,
    );
  }

  @Post('admins')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Superadmin creates an admin + shop' })
  createAdminWithShop(@Body() dto: CreateAdminWithShopDto) {
    return this.users.createAdminWithShop(dto);
  }

  @Post('superadmins')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Superadmin creates another superadmin' })
  createSuperadmin(
    @Req() req: AuthedRequest,
    @Body() dto: CreateSuperadminDto,
  ) {
    return this.users.createSuperadmin(req.user!, dto);
  }

  @Get('manage')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'List users (all roles) for management (SUPERADMIN only). Supports pagination and filters.',
  })
  listUsers(
    @Req() req: AuthedRequest,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('q') q?: string,
    @Query('role') role?: string,
    @Query('isActive') isActive?: string,
  ) {
    const p = page != null ? Number(page) : null;
    const l = limit != null ? Number(limit) : null;
    const wantsPaged =
      (p != null && Number.isFinite(p)) || (l != null && Number.isFinite(l));

    const pageNum =
      p != null && Number.isFinite(p) ? Math.max(1, Math.floor(p)) : 1;
    const limitNum =
      l != null && Number.isFinite(l)
        ? Math.min(100, Math.max(1, Math.floor(l)))
        : 20;

    if (!wantsPaged) {
      return this.users.listUsersPaged(req.user!, {
        page: 1,
        limit: 50,
        q: (q ?? '').toString(),
        role: role?.toString(),
        isActive: isActive?.toString(),
      });
    }

    return this.users.listUsersPaged(req.user!, {
      page: pageNum,
      limit: limitNum,
      q: (q ?? '').toString(),
      role: role?.toString(),
      isActive: isActive?.toString(),
    });
  }

  @Post('manage/:id/active')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'Activate/deactivate a user (any role). Tokens are revoked on deactivate.',
  })
  setUserActive(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: SetUserActiveDto,
  ) {
    return this.users.setUserActive(req.user!, id, dto.isActive);
  }

  @Patch('manage/:id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Update a user details (SUPERADMIN only)' })
  updateManagedUser(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: UpdateManagedUserDto,
  ) {
    return this.users.updateManagedUser(req.user!, id, dto);
  }

  @Post('shops/:id/approve')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Superadmin approves a pending shop' })
  approveShop(@Param('id') id: string) {
    return this.users.approveShop(id);
  }

  @Post('shops/:id/reject')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Superadmin rejects a pending shop (deactivates it)',
  })
  rejectShop(@Param('id') id: string, @Body() dto: RejectShopDto) {
    return this.users.rejectShop(id, dto.reason);
  }

  @Post('shops/:id/active')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Superadmin activates/deactivates a shop' })
  setShopActive(@Param('id') id: string, @Body() dto: SetShopActiveDto) {
    return this.users.setShopActive(id, dto.isActive);
  }

  @Post('shops/:id/status')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Superadmin changes shop status (PENDING/APPROVED/REJECTED)',
  })
  setShopStatus(@Param('id') id: string, @Body() dto: SetShopStatusDto) {
    return this.users.setShopStatus(id, dto);
  }

  @Post('shops/:id/resubmit')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'Admin resubmits a rejected shop for approval (REJECTED -> PENDING)',
  })
  resubmitShop(@Req() req: AuthedRequest, @Param('id') id: string) {
    return this.users.resubmitRejectedShopForAdmin(req.user!.id, id);
  }

  @Delete('shops/:id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Superadmin archives a shop (soft delete)' })
  deleteShop(@Param('id') id: string) {
    return this.users.archiveShop(id);
  }

  @Post('subadmins')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Admin creates a subadmin for owned shops (all/selected)',
  })
  createSubadmin(@Req() req: AuthedRequest, @Body() dto: CreateSubadminDto) {
    return this.users.createSubadminForAdmin(req.user!.id, dto);
  }

  @Get('subadmins')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Admin lists their subadmins' })
  listSubadmins(@Req() req: AuthedRequest) {
    return this.users.listSubadminsForAdmin(req.user!.id);
  }

  @Patch('subadmins/:id/shops')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Admin updates subadmin shop assignments' })
  updateSubadminShops(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: UpdateSubadminShopsDto,
  ) {
    return this.users.updateSubadminShopsForAdmin(req.user!.id, id, dto);
  }

  @Patch('subadmins/:id/permissions')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Admin updates subadmin permissions' })
  updateSubadminPermissions(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: UpdateSubadminPermissionsDto,
  ) {
    return this.users.updateSubadminPermissionsForAdmin(req.user!.id, id, dto);
  }

  @Delete('subadmins/:id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Admin removes a subadmin' })
  deleteSubadmin(@Req() req: AuthedRequest, @Param('id') id: string) {
    return this.users.deleteSubadminForAdmin(req.user!.id, id);
  }

  @Post('customers')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.SUBADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({ description: 'Create a customer for current shop' })
  createCustomer(@Req() req: AuthedRequest, @Body() dto: CreateCustomerDto) {
    return this.users.createCustomerForShop(req.user!, dto);
  }

  @Get('customers')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN, UserRole.ADMIN, UserRole.SUBADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'List customers (ADMIN/SUBADMIN: current shop, SUPERADMIN: all shops unless shopId is provided). If page/limit provided, returns paginated.',
  })
  listCustomers(
    @Req() req: AuthedRequest,
    @Query('shopId') shopId?: string,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('q') q?: string,
    @Query('isActive') isActive?: string,
    @Query('isCashiUser') isCashiUser?: string,
    @Query('couponOnly') couponOnly?: string,
  ) {
    const p = page != null ? Number(page) : null;
    const l = limit != null ? Number(limit) : null;
    const wantsPaged =
      (p != null && Number.isFinite(p)) || (l != null && Number.isFinite(l));

    if (!wantsPaged) return this.users.listCustomers(req.user!, shopId);

    const pageNum =
      p != null && Number.isFinite(p) ? Math.max(1, Math.floor(p)) : 1;
    const limitNum =
      l != null && Number.isFinite(l)
        ? Math.min(100, Math.max(1, Math.floor(l)))
        : 20;

    return this.users.listCustomersPaged(req.user!, {
      shopId: shopId?.toString(),
      page: pageNum,
      limit: limitNum,
      q: (q ?? '').toString(),
      isActive: isActive?.toString(),
      isCashiUser: isCashiUser?.toString(),
      couponOnly: couponOnly?.toString(),
    });
  }

  @Post('customers/:id/active')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN, UserRole.ADMIN, UserRole.SUBADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'Activate/deactivate a customer (token stops working when inactive)',
  })
  setCustomerActive(
    @Req() req: AuthedRequest,
    @Param('id') id: string,
    @Body() dto: SetCustomerActiveDto,
  ) {
    return this.users.setCustomerActive(req.user!, id, dto.isActive);
  }

  @Get('customers/lookup')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.SUPERADMIN, UserRole.ADMIN, UserRole.SUBADMIN)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description:
      'Lookup customer by phone or id for current shop (or shopId for SUPERADMIN)',
  })
  lookupCustomer(
    @Req() req: AuthedRequest,
    @Query('phone') phone?: string,
    @Query('id') id?: string,
    @Query('shopId') shopId?: string,
  ) {
    if (id != null && String(id).trim()) {
      return this.users.lookupCustomerById(req.user!, String(id), shopId);
    }
    return this.users.lookupCustomerByPhone(
      req.user!,
      String(phone ?? ''),
      shopId,
    );
  }

  // Keep dynamic ":id" routes at the end to avoid shadowing static routes (e.g. /subadmins).
  @Get(':id')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('bearer')
  @ApiOkResponse({
    description: 'Get single user details (scoped by role/shop)',
  })
  getUser(@Req() req: AuthedRequest, @Param('id') id: string) {
    return this.users.getUserById(req.user!, id);
  }
}
