import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { z } from 'zod';
import { CreateContractSchema, UpdateContractSchema, CreateTariffSchema, UpdateTariffSchema, CreateManualChargeSchema, ChargeQuerySchema, WaiveChargeSchema, RunInvoiceSchema, InvoiceQuerySchema, IssueInvoiceSchema, RecordPaymentSchema, CreateCreditNoteSchema, StorageReportQuerySchema, PaginationQuery, DateOnly } from '@wms/shared';
import { RequirePermissions, ApiKeyScopes, RequireFeature } from '@/common/auth/decorators';
import { zodBody, zodQuery } from '@/common/validation/zod.pipe';
import { ContractsService } from './contracts.service';
import { ChargeCaptureService } from './charge-capture.service';
import { StorageBillingService } from './storage-billing.service';
import { InvoicesService } from './invoices.service';

const ChargeListQuery = ChargeQuerySchema.merge(PaginationQuery);
const InvoiceListQuery = InvoiceQuerySchema.merge(PaginationQuery);
const ReasonSchema = z.object({ reason: z.string().min(1).max(500) });
const SnapshotSchema = z.object({ date: DateOnly.optional() });

@ApiTags('billing')
@ApiBearerAuth()
@RequireFeature('billing.3pl')
@Controller('billing')
export class BillingController {
  constructor(
    private readonly contracts: ContractsService,
    private readonly charges: ChargeCaptureService,
    private readonly storage: StorageBillingService,
    private readonly invoices: InvoicesService,
  ) {}

  @Get('contracts') @RequirePermissions('billing.read') listContracts(@Query('storerId') storerId?: string) { return this.contracts.list(storerId); }
  @Get('contracts/:id') @RequirePermissions('billing.read') getContract(@Param('id') id: string) { return this.contracts.get(id); }
  @Post('contracts') @RequirePermissions('billing.manage') createContract(@Body(zodBody(CreateContractSchema)) dto: z.infer<typeof CreateContractSchema>) { return this.contracts.create(dto); }
  @Patch('contracts/:id') @RequirePermissions('billing.manage') updateContract(@Param('id') id: string, @Body(zodBody(UpdateContractSchema)) dto: z.infer<typeof UpdateContractSchema>) { return this.contracts.update(id, dto); }
  @Post('contracts/:id/tariffs') @RequirePermissions('billing.manage') addTariff(@Param('id') id: string, @Body(zodBody(CreateTariffSchema)) dto: z.infer<typeof CreateTariffSchema>) { return this.contracts.addTariff(id, dto); }
  @Patch('tariffs/:id') @RequirePermissions('billing.manage') updateTariff(@Param('id') id: string, @Body(zodBody(UpdateTariffSchema)) dto: z.infer<typeof UpdateTariffSchema>) { return this.contracts.updateTariff(id, dto); }
  @Delete('tariffs/:id') @RequirePermissions('billing.manage') deleteTariff(@Param('id') id: string) { return this.contracts.deleteTariff(id); }

  @Get('charges') @RequirePermissions('billing.read') @ApiKeyScopes('billing:read') listCharges(@Query(zodQuery(ChargeListQuery)) q: z.infer<typeof ChargeListQuery>) { return this.invoices.listCharges(q); }
  @Post('charges') @RequirePermissions('billing.manage') manualCharge(@Body(zodBody(CreateManualChargeSchema)) dto: z.infer<typeof CreateManualChargeSchema>) { return this.charges.manual(dto); }
  @Post('charges/:id/waive') @RequirePermissions('billing.manage') waive(@Param('id') id: string, @Body(zodBody(WaiveChargeSchema)) dto: z.infer<typeof WaiveChargeSchema>) { return this.charges.waive(id, dto.reason); }

  @Get('storage/report') @RequirePermissions('billing.read') @ApiKeyScopes('billing:read') storageReport(@Query(zodQuery(StorageReportQuerySchema)) q: z.infer<typeof StorageReportQuerySchema>) { return this.storage.report(q); }
  @Post('storage/snapshot') @RequirePermissions('billing.manage') snapshot(@Body(zodBody(SnapshotSchema)) dto: z.infer<typeof SnapshotSchema>) { return this.storage.snapshotTenant(dto.date ?? new Date().toISOString().slice(0, 10)); }

  @Get('invoices') @RequirePermissions('billing.read') @ApiKeyScopes('billing:read') listInvoices(@Query(zodQuery(InvoiceListQuery)) q: z.infer<typeof InvoiceListQuery>) { return this.invoices.list(q); }
  @Get('invoices/:id') @RequirePermissions('billing.read') @ApiKeyScopes('billing:read') getInvoice(@Param('id') id: string) { return this.invoices.get(id); }
  @Post('invoices/run') @RequirePermissions('billing.invoice') run(@Body(zodBody(RunInvoiceSchema)) dto: z.infer<typeof RunInvoiceSchema>) { return this.invoices.run(dto); }
  @Post('invoices/:id/issue') @RequirePermissions('billing.invoice') issue(@Param('id') id: string, @Body(zodBody(IssueInvoiceSchema)) dto: z.infer<typeof IssueInvoiceSchema>) { return this.invoices.issue(id, dto); }
  @Post('invoices/:id/cancel') @RequirePermissions('billing.invoice') cancelInvoice(@Param('id') id: string, @Body(zodBody(ReasonSchema)) dto: z.infer<typeof ReasonSchema>) { return this.invoices.cancel(id, dto.reason); }
  @Post('invoices/:id/payments') @RequirePermissions('billing.invoice') payment(@Param('id') id: string, @Body(zodBody(RecordPaymentSchema)) dto: z.infer<typeof RecordPaymentSchema>) { return this.invoices.recordPayment(id, dto); }
  @Post('invoices/:id/credit-notes') @RequirePermissions('billing.invoice') creditNote(@Param('id') id: string, @Body(zodBody(CreateCreditNoteSchema)) dto: z.infer<typeof CreateCreditNoteSchema>) { return this.invoices.creditNote(id, dto); }
  @Post('invoices/:id/dispute') @RequirePermissions('billing.manage') dispute(@Param('id') id: string, @Body(zodBody(ReasonSchema)) dto: z.infer<typeof ReasonSchema>) { return this.invoices.dispute(id, dto.reason); }
}
