import {
  Body,
  Controller,
  Get,
  Param,
  ParseUUIDPipe,
  Post,
  Query,
} from '@nestjs/common';
import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
import { CurrentUser } from '@edr/api-common';
import type { TCurrentUser } from '@tria-plc/api-common/modules/auth/types/current-user.type';

import {
  WagonTransferCancel,
  WagonTransferCloseShort,
  WagonTransferFulfill,
  WagonTransferHistoryAll,
  WagonTransferRequest,
  WagonTransferView,
} from '../../common/booking-guards';
import { BulkFulfillTransferRequestsDto } from './dto/bulk-fulfill-transfer-requests.dto';
import { CloseShortTransferRequestDto } from './dto/close-short-transfer-request.dto';
import { CreateTransferRequestDto } from './dto/create-transfer-request.dto';
import { FulfillTransferRequestDto } from './dto/fulfill-transfer-request.dto';
import { ListTransferRequestsQueryDto } from './dto/list-transfer-requests-query.dto';
import { WagonTransferRequestsService } from './wagon-transfer-requests.service';

/** Query-string number, or undefined when absent/garbage (service defaults it). */
const toInt = (value?: string): number | undefined => {
  const n = Number.parseInt(String(value ?? ''), 10);
  return Number.isFinite(n) && n > 0 ? n : undefined;
};

/**
 * The wagon-transfer desk. A requester (transfer_request) files a count-only
 * request; OCC (transfer_fulfill) moves wagons against it in as many
 * instalments as the source yard allows, and closes it short
 * (transfer_close_short) when the yard has no more to give. Separate top-level
 * path so it never collides with `wagons/:id`.
 */
@ApiTags('wagon-transfer-requests')
@Controller('wagon-transfer-requests')
@WagonTransferView()
export class WagonTransferRequestsController {
  constructor(private readonly service: WagonTransferRequestsService) {}

  @Post()
  @WagonTransferRequest()
  @ApiOperation({ summary: 'File a count-only wagon-transfer request' })
  create(
    @Body() dto: CreateTransferRequestDto,
    @CurrentUser() user: TCurrentUser,
  ) {
    return this.service.createRequest(dto, user?.id);
  }

  @Get()
  @ApiOperation({
    summary:
      'Transfer desk list — paginated, filterable by status (comma-separated), yards and wagon type',
  })
  list(@Query() query: ListTransferRequestsQueryDto) {
    return this.service.listRequests(query);
  }

  // NOTE: static routes (`history`, `bulk-fulfill`) MUST stay above `@Get(':id')`
  // — Express matches in declaration order, so they would otherwise be captured
  // by the `:id` param route (and rejected by ParseUUIDPipe).
  @Post('bulk-fulfill')
  @WagonTransferFulfill()
  @ApiOperation({
    summary:
      'OCC: accept-and-execute a subset of pending requests (auto-picks available wagons; the rest stay PENDING)',
  })
  bulkFulfill(
    @Body() dto: BulkFulfillTransferRequestsDto,
    @CurrentUser() user: TCurrentUser,
  ) {
    return this.service.bulkFulfill(dto.requestIds, user?.id);
  }

  // NOTE: the two `history` routes MUST stay above `@Get(':id')` — Express
  // matches in declaration order, so `/history` would otherwise be captured by
  // the `:id` param route (and rejected by ParseUUIDPipe).
  @Get('history')
  @ApiQuery({ name: 'page', required: false })
  @ApiQuery({ name: 'pageSize', required: false })
  @ApiOperation({
    summary: "Caller's own transfer history (requests filed/fulfilled + wagons moved)",
  })
  myHistory(
    @CurrentUser() user: TCurrentUser,
    @Query('page') page?: string,
    @Query('pageSize') pageSize?: string,
  ) {
    // Never fall through to the all-staff view: getHistory(undefined) means
    // "everyone", so a missing caller id must return empty, not leak scope.
    if (!user?.id) {
      return {
        requests: [],
        movements: [],
        meta: {
          page: 1,
          pageSize: 20,
          requestsTotal: 0,
          movementsTotal: 0,
          totalPages: 1,
        },
      };
    }
    return this.service.getHistory(user.id, toInt(page), toInt(pageSize));
  }

  @Get('history/all')
  @WagonTransferHistoryAll()
  @ApiQuery({ name: 'userId', required: false })
  @ApiQuery({ name: 'page', required: false })
  @ApiQuery({ name: 'pageSize', required: false })
  @ApiOperation({
    summary: "Admin: any/all staff's transfer history (optional ?userId filter)",
  })
  allHistory(
    @Query('userId') userId?: string,
    @Query('page') page?: string,
    @Query('pageSize') pageSize?: string,
  ) {
    return this.service.getHistory(userId, toInt(page), toInt(pageSize));
  }

  @Get(':id')
  @ApiOperation({ summary: 'Get one transfer request' })
  findOne(@Param('id', ParseUUIDPipe) id: string) {
    return this.service.findById(id);
  }

  @Post(':id/fulfill')
  @WagonTransferFulfill()
  @ApiOperation({ summary: 'OCC: pick wagons and execute the transfer' })
  fulfill(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: FulfillTransferRequestDto,
    @CurrentUser() user: TCurrentUser,
  ) {
    return this.service.fulfillRequest(id, dto, user?.id);
  }

  @Post(':id/close-short')
  @WagonTransferCloseShort()
  @ApiOperation({
    summary:
      'OCC: end the request with fewer wagons than asked for — what moved stays, the requester is told the shortfall',
  })
  closeShort(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: CloseShortTransferRequestDto,
    @CurrentUser() user: TCurrentUser,
  ) {
    return this.service.closeShort(id, dto, user?.id);
  }

  @Post(':id/cancel')
  @WagonTransferCancel()
  @ApiOperation({
    summary:
      'Withdraw a request that has not moved any wagon yet (use close-short once wagons have moved)',
  })
  cancel(@Param('id', ParseUUIDPipe) id: string) {
    return this.service.cancelRequest(id);
  }
}
