import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { z } from 'zod';
import {
  InventoryQuerySchema, MovementQuerySchema, MoveStockSchema, CreateLpnSchema, PlaceHoldSchema, ReleaseHoldSchema, CreateAdjustmentSchema, DecideAdjustmentSchema,
  CreateCycleCountSchema, SubmitCountSchema, ReviewCountLinesSchema, SerialQuerySchema, PaginationQuery, Uuid,
} from '@wms/shared';
import { RequirePermissions, ApiKeyScopes } from '@/common/auth/decorators';
import { zodBody, zodQuery } from '@/common/validation/zod.pipe';
import { InventoryService } from './inventory.service';
import { AdjustmentsService } from './adjustments.service';
import { CountsService } from './counts.service';

const InvQuery = InventoryQuerySchema.merge(PaginationQuery);
const MovQuery = MovementQuerySchema.merge(PaginationQuery);
const SerialQuery = SerialQuerySchema.merge(PaginationQuery);
const AdjQuery = PaginationQuery.extend({ warehouseId: Uuid.optional(), storerId: Uuid.optional(), status: z.string().optional() });
const CountQuery = PaginationQuery.extend({ warehouseId: Uuid.optional(), status: z.string().optional() });

@ApiTags('inventory')
@ApiBearerAuth()
@Controller('inventory')
export class InventoryController {
  constructor(
    private readonly inventory: InventoryService,
    private readonly adjustments: AdjustmentsService,
    private readonly counts: CountsService,
  ) {}

  @Get('balances') @RequirePermissions('inventory.read') @ApiKeyScopes('inventory:read')
  balances(@Query(zodQuery(InvQuery)) q: z.infer<typeof InvQuery>) { return this.inventory.query(q); }

  @Get('movements') @RequirePermissions('inventory.read') @ApiKeyScopes('inventory:read')
  movements(@Query(zodQuery(MovQuery)) q: z.infer<typeof MovQuery>) { return this.inventory.movements(q); }

  @Get('serials') @RequirePermissions('inventory.read') @ApiKeyScopes('serials:read')
  serials(@Query(zodQuery(SerialQuery)) q: z.infer<typeof SerialQuery>) { return this.inventory.serials(q); }

  @Get('lpns/:lpnNumber') @RequirePermissions('inventory.read')
  lpn(@Param('lpnNumber') lpnNumber: string) { return this.inventory.lpnInquiry(lpnNumber); }

  @Get('locations/:warehouseId/:code') @RequirePermissions('inventory.read')
  location(@Param('warehouseId') warehouseId: string, @Param('code') code: string) { return this.inventory.locationInquiry(warehouseId, code); }

  @Post('lpns') @RequirePermissions('inventory.move')
  createLpns(@Body(zodBody(CreateLpnSchema)) dto: z.infer<typeof CreateLpnSchema>) { return this.inventory.createLpns(dto); }

  @Post('moves') @RequirePermissions('inventory.move')
  move(@Body(zodBody(MoveStockSchema)) dto: z.infer<typeof MoveStockSchema>) { return this.inventory.move(dto); }

  // ---- holds ----
  @Get('holds') @RequirePermissions('inventory.read')
  holds(@Query('warehouseId') warehouseId: string, @Query('active') active?: string) { return this.inventory.listHolds(warehouseId, active !== 'false'); }

  @Post('holds') @RequirePermissions('inventory.hold')
  placeHold(@Body(zodBody(PlaceHoldSchema)) dto: z.infer<typeof PlaceHoldSchema>) { return this.inventory.placeHold(dto); }

  @Post('holds/:id/release') @RequirePermissions('inventory.hold')
  releaseHold(@Param('id') id: string, @Body(zodBody(ReleaseHoldSchema)) dto: z.infer<typeof ReleaseHoldSchema>) { return this.inventory.releaseHold(id, dto); }

  // ---- adjustments ----
  @Get('adjustments') @RequirePermissions('inventory.read')
  listAdjustments(@Query(zodQuery(AdjQuery)) q: z.infer<typeof AdjQuery>) { return this.adjustments.list(q); }

  @Get('adjustments/:id') @RequirePermissions('inventory.read')
  getAdjustment(@Param('id') id: string) { return this.adjustments.get(id); }

  @Post('adjustments') @RequirePermissions('inventory.adjust.request')
  createAdjustment(@Body(zodBody(CreateAdjustmentSchema)) dto: z.infer<typeof CreateAdjustmentSchema>) { return this.adjustments.create(dto); }

  @Post('adjustments/:id/submit') @RequirePermissions('inventory.adjust.request')
  submitAdjustment(@Param('id') id: string) { return this.adjustments.submit(id); }

  @Post('adjustments/:id/decide') @RequirePermissions('inventory.adjust.approve')
  decideAdjustment(@Param('id') id: string, @Body(zodBody(DecideAdjustmentSchema)) dto: z.infer<typeof DecideAdjustmentSchema>) { return this.adjustments.decide(id, dto); }

  @Post('adjustments/:id/cancel') @RequirePermissions('inventory.adjust.request')
  cancelAdjustment(@Param('id') id: string) { return this.adjustments.cancel(id); }

  // ---- counts ----
  @Get('counts') @RequirePermissions('inventory.read')
  listCounts(@Query(zodQuery(CountQuery)) q: z.infer<typeof CountQuery>) { return this.counts.list(q); }

  @Get('counts/:id') @RequirePermissions('inventory.read')
  getCount(@Param('id') id: string) { return this.counts.get(id); }

  @Post('counts') @RequirePermissions('inventory.count.manage')
  createCount(@Body(zodBody(CreateCycleCountSchema)) dto: z.infer<typeof CreateCycleCountSchema>) { return this.counts.create(dto); }

  @Post('counts/:id/release') @RequirePermissions('inventory.count.manage')
  releaseCount(@Param('id') id: string) { return this.counts.release(id); }

  @Get('counts/:id/locations/:code') @RequirePermissions('inventory.count.perform')
  countSheet(@Param('id') id: string, @Param('code') code: string) { return this.counts.locationSheet(id, code); }

  @Post('counts/:id/submit') @RequirePermissions('inventory.count.perform')
  submitCount(@Param('id') id: string, @Body(zodBody(SubmitCountSchema)) dto: z.infer<typeof SubmitCountSchema>) { return this.counts.submit(id, dto); }

  @Post('counts/:id/review') @RequirePermissions('inventory.count.manage')
  reviewCount(@Param('id') id: string, @Body(zodBody(ReviewCountLinesSchema)) dto: z.infer<typeof ReviewCountLinesSchema>) { return this.counts.review(id, dto); }

  @Post('counts/:id/cancel') @RequirePermissions('inventory.count.manage')
  cancelCount(@Param('id') id: string) { return this.counts.cancel(id); }
}
