import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { z } from 'zod';
import {
  CreateWarehouseSchema, UpdateWarehouseSchema, CreateZoneSchema, UpdateZoneSchema, CreateLocationSchema, UpdateLocationSchema, GenerateLocationsSchema,
  CreateStorerSchema, UpdateStorerSchema, CreateSupplierSchema, UpdateSupplierSchema, CreateCustomerSchema, UpdateCustomerSchema, CreateCarrierSchema, UpdateCarrierSchema,
  CreateSkuSchema, UpdateSkuSchema, UpsertSlottingSchema, CreatePutawayRuleSchema, UpdatePutawayRuleSchema, CreateAllocationRuleSchema, UpdateAllocationRuleSchema,
  CreateReasonCodeSchema, UpdateReasonCodeSchema, PaginationQuery, Uuid,
} from '@wms/shared';
import { RequirePermissions } from '@/common/auth/decorators';
import { zodBody, zodQuery } from '@/common/validation/zod.pipe';
import { WarehousesService } from './warehouses.service';
import { PartnersService } from './partners.service';
import { SkusService } from './skus.service';
import { RulesService } from './rules.service';

const LocationListQuery = PaginationQuery.extend({ zoneId: Uuid.optional(), usage: z.string().optional(), status: z.string().optional() });
const StorerScopedQuery = PaginationQuery.extend({ storerId: Uuid.optional() });
const SkuListQuery = StorerScopedQuery.extend({ status: z.string().optional(), category: z.string().optional() });

@ApiTags('master-data')
@ApiBearerAuth()
@Controller()
export class MasterDataController {
  constructor(
    private readonly wh: WarehousesService,
    private readonly partners: PartnersService,
    private readonly skus: SkusService,
    private readonly rules: RulesService,
  ) {}

  // ---------- warehouses / zones / locations ----------
  @Get('warehouses') @RequirePermissions('masterdata.read') listWarehouses() { return this.wh.list(); }
  @Get('warehouses/:id') @RequirePermissions('masterdata.read') getWarehouse(@Param('id') id: string) { return this.wh.get(id); }
  @Post('warehouses') @RequirePermissions('masterdata.manage') createWarehouse(@Body(zodBody(CreateWarehouseSchema)) dto: z.infer<typeof CreateWarehouseSchema>) { return this.wh.create(dto); }
  @Patch('warehouses/:id') @RequirePermissions('masterdata.manage') updateWarehouse(@Param('id') id: string, @Body(zodBody(UpdateWarehouseSchema)) dto: z.infer<typeof UpdateWarehouseSchema>) { return this.wh.update(id, dto); }

  @Get('warehouses/:id/zones') @RequirePermissions('masterdata.read') listZones(@Param('id') id: string) { return this.wh.listZones(id); }
  @Post('zones') @RequirePermissions('masterdata.manage') createZone(@Body(zodBody(CreateZoneSchema)) dto: z.infer<typeof CreateZoneSchema>) { return this.wh.createZone(dto); }
  @Patch('zones/:id') @RequirePermissions('masterdata.manage') updateZone(@Param('id') id: string, @Body(zodBody(UpdateZoneSchema)) dto: z.infer<typeof UpdateZoneSchema>) { return this.wh.updateZone(id, dto); }

  @Get('warehouses/:id/locations') @RequirePermissions('masterdata.read')
  listLocations(@Param('id') id: string, @Query(zodQuery(LocationListQuery)) q: z.infer<typeof LocationListQuery>) { return this.wh.listLocations(id, q); }
  @Post('locations') @RequirePermissions('masterdata.manage') createLocation(@Body(zodBody(CreateLocationSchema)) dto: z.infer<typeof CreateLocationSchema>) { return this.wh.createLocation(dto); }
  @Post('locations/generate') @RequirePermissions('masterdata.manage') generateLocations(@Body(zodBody(GenerateLocationsSchema)) dto: z.infer<typeof GenerateLocationsSchema>) { return this.wh.generateLocations(dto); }
  @Patch('locations/:id') @RequirePermissions('masterdata.manage') updateLocation(@Param('id') id: string, @Body(zodBody(UpdateLocationSchema)) dto: z.infer<typeof UpdateLocationSchema>) { return this.wh.updateLocation(id, dto); }

  // ---------- partners ----------
  @Get('storers') @RequirePermissions('masterdata.read') listStorers(@Query(zodQuery(PaginationQuery)) q: z.infer<typeof PaginationQuery>) { return this.partners.listStorers(q); }
  @Get('storers/:id') @RequirePermissions('masterdata.read') getStorer(@Param('id') id: string) { return this.partners.getStorer(id); }
  @Post('storers') @RequirePermissions('masterdata.manage') createStorer(@Body(zodBody(CreateStorerSchema)) dto: z.infer<typeof CreateStorerSchema>) { return this.partners.createStorer(dto); }
  @Patch('storers/:id') @RequirePermissions('masterdata.manage') updateStorer(@Param('id') id: string, @Body(zodBody(UpdateStorerSchema)) dto: z.infer<typeof UpdateStorerSchema>) { return this.partners.updateStorer(id, dto); }

  @Get('suppliers') @RequirePermissions('masterdata.read') listSuppliers(@Query(zodQuery(StorerScopedQuery)) q: z.infer<typeof StorerScopedQuery>) { return this.partners.listSuppliers(q); }
  @Post('suppliers') @RequirePermissions('masterdata.manage') createSupplier(@Body(zodBody(CreateSupplierSchema)) dto: z.infer<typeof CreateSupplierSchema>) { return this.partners.createSupplier(dto); }
  @Patch('suppliers/:id') @RequirePermissions('masterdata.manage') updateSupplier(@Param('id') id: string, @Body(zodBody(UpdateSupplierSchema)) dto: z.infer<typeof UpdateSupplierSchema>) { return this.partners.updateSupplier(id, dto); }

  @Get('customers') @RequirePermissions('masterdata.read') listCustomers(@Query(zodQuery(StorerScopedQuery)) q: z.infer<typeof StorerScopedQuery>) { return this.partners.listCustomers(q); }
  @Post('customers') @RequirePermissions('masterdata.manage') createCustomer(@Body(zodBody(CreateCustomerSchema)) dto: z.infer<typeof CreateCustomerSchema>) { return this.partners.createCustomer(dto); }
  @Patch('customers/:id') @RequirePermissions('masterdata.manage') updateCustomer(@Param('id') id: string, @Body(zodBody(UpdateCustomerSchema)) dto: z.infer<typeof UpdateCustomerSchema>) { return this.partners.updateCustomer(id, dto); }

  @Get('carriers') @RequirePermissions('masterdata.read') listCarriers() { return this.partners.listCarriers(); }
  @Post('carriers') @RequirePermissions('masterdata.manage') createCarrier(@Body(zodBody(CreateCarrierSchema)) dto: z.infer<typeof CreateCarrierSchema>) { return this.partners.createCarrier(dto); }
  @Patch('carriers/:id') @RequirePermissions('masterdata.manage') updateCarrier(@Param('id') id: string, @Body(zodBody(UpdateCarrierSchema)) dto: z.infer<typeof UpdateCarrierSchema>) { return this.partners.updateCarrier(id, dto); }

  // ---------- skus ----------
  @Get('skus') @RequirePermissions('masterdata.read') listSkus(@Query(zodQuery(SkuListQuery)) q: z.infer<typeof SkuListQuery>) { return this.skus.list(q); }
  @Get('skus/:id') @RequirePermissions('masterdata.read') getSku(@Param('id') id: string) { return this.skus.get(id); }
  @Post('skus') @RequirePermissions('masterdata.manage') createSku(@Body(zodBody(CreateSkuSchema)) dto: z.infer<typeof CreateSkuSchema>) { return this.skus.create(dto); }
  @Patch('skus/:id') @RequirePermissions('masterdata.manage') updateSku(@Param('id') id: string, @Body(zodBody(UpdateSkuSchema)) dto: z.infer<typeof UpdateSkuSchema>) { return this.skus.update(id, dto); }
  @Put('slottings') @RequirePermissions('rules.manage') upsertSlotting(@Body(zodBody(UpsertSlottingSchema)) dto: z.infer<typeof UpsertSlottingSchema>) { return this.skus.upsertSlotting(dto); }
  @Delete('slottings/:id') @RequirePermissions('rules.manage') deleteSlotting(@Param('id') id: string) { return this.skus.deleteSlotting(id); }

  // ---------- rules ----------
  @Get('warehouses/:id/putaway-rules') @RequirePermissions('masterdata.read') listPutawayRules(@Param('id') id: string) { return this.rules.listPutawayRules(id); }
  @Post('putaway-rules') @RequirePermissions('rules.manage') createPutawayRule(@Body(zodBody(CreatePutawayRuleSchema)) dto: z.infer<typeof CreatePutawayRuleSchema>) { return this.rules.createPutawayRule(dto); }
  @Patch('putaway-rules/:id') @RequirePermissions('rules.manage') updatePutawayRule(@Param('id') id: string, @Body(zodBody(UpdatePutawayRuleSchema)) dto: z.infer<typeof UpdatePutawayRuleSchema>) { return this.rules.updatePutawayRule(id, dto); }
  @Delete('putaway-rules/:id') @RequirePermissions('rules.manage') deletePutawayRule(@Param('id') id: string) { return this.rules.deletePutawayRule(id); }

  @Get('allocation-rules') @RequirePermissions('masterdata.read') listAllocationRules() { return this.rules.listAllocationRules(); }
  @Post('allocation-rules') @RequirePermissions('rules.manage') createAllocationRule(@Body(zodBody(CreateAllocationRuleSchema)) dto: z.infer<typeof CreateAllocationRuleSchema>) { return this.rules.createAllocationRule(dto); }
  @Patch('allocation-rules/:id') @RequirePermissions('rules.manage') updateAllocationRule(@Param('id') id: string, @Body(zodBody(UpdateAllocationRuleSchema)) dto: z.infer<typeof UpdateAllocationRuleSchema>) { return this.rules.updateAllocationRule(id, dto); }
  @Delete('allocation-rules/:id') @RequirePermissions('rules.manage') deleteAllocationRule(@Param('id') id: string) { return this.rules.deleteAllocationRule(id); }

  @Get('reason-codes') @RequirePermissions('masterdata.read') listReasonCodes(@Query('type') type?: string) { return this.rules.listReasonCodes(type); }
  @Post('reason-codes') @RequirePermissions('masterdata.manage') createReasonCode(@Body(zodBody(CreateReasonCodeSchema)) dto: z.infer<typeof CreateReasonCodeSchema>) { return this.rules.createReasonCode(dto); }
  @Patch('reason-codes/:id') @RequirePermissions('masterdata.manage') updateReasonCode(@Param('id') id: string, @Body(zodBody(UpdateReasonCodeSchema)) dto: z.infer<typeof UpdateReasonCodeSchema>) { return this.rules.updateReasonCode(id, dto); }
}
