import {
  Body,
  Controller,
  Delete,
  Get,
  Headers,
  HttpCode,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Query,
  RawBodyRequest,
  Req,
  UnauthorizedException,
  UseGuards,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Request } from 'express';
import AuthGuard, { AllowUnauthorizedRequest } from '../../lib/auth.guard';
import { TemporalService } from './temporal.service';
import {
  ExecuteWorkflowDto,
  SignalWorkflowDto,
} from './workflow-execution.dto';
import { ImportPayloadDto } from './workflow-export.dto';
import { WorkflowExportService } from './workflow-export.service';
import {
  CreateWorkflowDto,
  PublishVersionDto,
  SaveDraftDto,
  ToggleEnabledDto,
  UpdateWorkflowDto,
  WorkflowListQueryDto,
} from './workflow.dto';
import { WorkflowService } from './workflow.service';

@ApiTags('workflow')
@UseGuards(AuthGuard)
@Controller('workflow')
export class WorkflowController {
  constructor(
    private readonly workflowService: WorkflowService,
    private readonly temporalService: TemporalService,
    private readonly workflowExportService: WorkflowExportService,
  ) {}

  @Get()
  findAll(@Query() query: WorkflowListQueryDto) {
    return this.workflowService.findMany({
      status: query.status,
      instanceStatus: query.instanceStatus,
      name: query.name,
      skip: query.skip,
      limit: query.limit,
    });
  }

  @Get('overview')
  getOverview() {
    return this.workflowService.getOverview();
  }

  // ─── Export/Import Endpoints ──────────────────────────────────────────────────

  @Post(':id/export-token')
  async generateExportToken(@Param('id', ParseIntPipe) id: number, @Req() req) {
    const baseUrl = `${req.protocol}://${req.get('host')}/api`;
    return this.workflowExportService.generateExportToken(BigInt(id), baseUrl);
  }

  @Get('export/:code')
  @AllowUnauthorizedRequest()
  async getExportPayload(
    @Param('code') code: string,
    @Query('token') token: string,
  ) {
    if (!token) {
      throw new UnauthorizedException('Export token is required');
    }
    return this.workflowExportService.getExportPayload(code, token);
  }

  @Post('import-preview')
  async importPreview(@Body() dto: ImportPayloadDto) {
    return this.workflowExportService.importPreview(dto.payload);
  }

  @Post('import-confirm')
  async importConfirm(@Body() dto: ImportPayloadDto, @Req() req) {
    return this.workflowExportService.importConfirm(
      dto.payload,
      req.user.userid,
    );
  }

  @Post()
  create(@Req() req, @Body() dto: CreateWorkflowDto) {
    return this.workflowService.create(dto, req.user.userid);
  }

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.workflowService.findOne(BigInt(id));
  }

  @Patch(':id')
  update(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateWorkflowDto,
  ) {
    return this.workflowService.update(BigInt(id), dto);
  }

  @Patch(':id/save')
  saveDraft(@Param('id', ParseIntPipe) id: number, @Body() dto: SaveDraftDto) {
    return this.workflowService.saveDraft(BigInt(id), dto.dsl);
  }

  @Patch(':id/toggle')
  toggleEnabled(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: ToggleEnabledDto,
  ) {
    return this.workflowService.toggleEnabled(BigInt(id), dto.enabled);
  }

  @Delete(':id')
  remove(@Param('id', ParseIntPipe) id: number) {
    return this.workflowService.remove(BigInt(id));
  }

  @Get(':id/versions')
  getVersions(@Param('id', ParseIntPipe) id: number) {
    return this.workflowService.getVersions(BigInt(id));
  }

  @Get(':id/versions/:version')
  getVersionDsl(
    @Param('id', ParseIntPipe) id: number,
    @Param('version', ParseIntPipe) version: number,
  ) {
    return this.workflowService.getVersionDsl(BigInt(id), version);
  }

  @Post(':id/publish')
  publish(
    @Param('id', ParseIntPipe) id: number,
    @Req() req,
    @Body() dto: PublishVersionDto,
  ) {
    return this.workflowService.publish(BigInt(id), dto, req.user.userid);
  }

  @Get(':id/instances')
  getInstances(
    @Param('id', ParseIntPipe) id: number,
    @Query() query: { status?: string; skip?: string; limit?: string },
  ) {
    return this.workflowService.getInstances(BigInt(id), {
      status: query.status,
      skip: query.skip ? Number(query.skip) : undefined,
      limit: query.limit ? Number(query.limit) : undefined,
    });
  }

  @Get(':id/stats')
  getStats(@Param('id', ParseIntPipe) id: number) {
    return this.workflowService.getStats(BigInt(id));
  }

  // ─── Execution Endpoints ──────────────────────────────────────────────────────

  @Post(':id/execute')
  async execute(
    @Param('id', ParseIntPipe) id: number,
    @Req() req,
    @Body() dto: ExecuteWorkflowDto,
  ) {
    return this.workflowService.executeWorkflow(
      BigInt(id),
      dto,
      req.user.userid,
      this.temporalService,
    );
  }

  @Get('instance/:instanceId')
  getInstanceDetail(@Param('instanceId', ParseIntPipe) instanceId: number) {
    return this.workflowService.getInstanceDetail(BigInt(instanceId));
  }

  @Post('instance/:instanceId/cancel')
  async cancelInstance(@Param('instanceId', ParseIntPipe) instanceId: number) {
    return this.workflowService.cancelInstance(
      BigInt(instanceId),
      this.temporalService,
    );
  }

  @Post('instance/:instanceId/retry')
  async retryInstance(
    @Param('instanceId', ParseIntPipe) instanceId: number,
    @Req() req,
  ) {
    return this.workflowService.retryInstance(
      BigInt(instanceId),
      req.user.userid,
      this.temporalService,
    );
  }

  @Post('instance/:instanceId/signal')
  async signalInstance(
    @Param('instanceId', ParseIntPipe) instanceId: number,
    @Body() dto: SignalWorkflowDto,
  ) {
    return this.workflowService.signalInstance(
      BigInt(instanceId),
      dto,
      this.temporalService,
    );
  }

  /**
   * Public webhook entry point.
   * Triggers any published workflow with a webhook_trigger node matching (path, method).
   * Caller must provide HMAC-SHA256 signature in X-Workflow-Signature header.
   *
   * Returns 202 Accepted with the list of created instance IDs (workflow runs async).
   */
  @Post('webhook/:path')
  @HttpCode(202)
  @AllowUnauthorizedRequest()
  async receiveWebhook(
    @Param('path') path: string,
    @Headers('x-workflow-signature') signature: string,
    @Headers('x-workflow-timestamp') timestamp: string,
    @Req() req: RawBodyRequest<Request>,
    @Body() body: any,
    @Query() query: any,
  ): Promise<{ instanceIds: string[] }> {
    return this.workflowService.receiveWebhook({
      path,
      method: req.method,
      signatureHeader: signature,
      timestampHeader: timestamp,
      rawBody: req.rawBody ?? Buffer.from(''),
      body,
      headers: req.headers as Record<string, any>,
      query,
    });
  }
}
