import {
  Body,
  Controller,
  Get,
  Param,
  Patch,
  Post,
  Put,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import { SkipThrottle } from '@nestjs/throttler';

import {
  whmcsChangePackageRequestSchema,
  whmcsCreateSsoRequestSchema,
  whmcsEventsAckRequestSchema,
  whmcsImportServiceRequestSchema,
  whmcsProvisionServiceRequestSchema,
  whmcsReconcileRequestSchema,
  whmcsSuspendRequestSchema,
  whmcsUpsertClientRequestSchema,
  whmcsUsageAdjustmentRequestSchema,
  whmcsUsageQuerySchema,
} from '@hexahost/contracts';
import { INTEGRATION_HEADERS } from '@hexahost/integration-auth';

import { SsoService } from '../../auth/sso.service';

import {
  WhmcsIntegrationGuard,
  type WhmcsAuthenticatedRequest,
} from './whmcs-integration.guard';
import { WhmcsIntegrationService } from './whmcs-integration.service';
import { WhmcsEventsService } from './whmcs-events.service';
import { WhmcsReconciliationService } from './whmcs-reconciliation.service';
import { WhmcsUsageService } from './whmcs-usage.service';

@Controller('integrations/whmcs')
@UseGuards(WhmcsIntegrationGuard)
@SkipThrottle()
export class WhmcsIntegrationController {
  constructor(
    private readonly whmcs: WhmcsIntegrationService,
    private readonly sso: SsoService,
    private readonly reconciliation: WhmcsReconciliationService,
    private readonly events: WhmcsEventsService,
    private readonly usage: WhmcsUsageService,
  ) {}

  @Get('health')
  health(@Req() request: WhmcsAuthenticatedRequest) {
    return this.whmcs.getHealth(request.whmcsInstallation!);
  }

  @Get('catalog/plans')
  listPlans() {
    return this.whmcs.listPlans();
  }

  @Put('clients/:externalClientId')
  upsertClient(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalClientId') externalClientId: string,
    @Body() body: unknown,
  ) {
    const parsed = whmcsUpsertClientRequestSchema.parse(body);
    return this.whmcs.upsertClient(
      request.whmcsInstallation!,
      externalClientId,
      parsed,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Post('services')
  provisionService(@Req() request: WhmcsAuthenticatedRequest, @Body() body: unknown) {
    const parsed = whmcsProvisionServiceRequestSchema.parse(body);
    return this.whmcs.provisionService(
      request.whmcsInstallation!,
      parsed,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Get('services/:externalServiceId')
  getService(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
  ) {
    return this.whmcs.getService(request.whmcsInstallation!, externalServiceId);
  }

  @Post('services/:externalServiceId/suspend')
  suspendService(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
    @Body() body: unknown,
  ) {
    const parsed = whmcsSuspendRequestSchema.parse(body ?? {});
    return this.whmcs.suspendService(
      request.whmcsInstallation!,
      externalServiceId,
      parsed.reason,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Post('services/:externalServiceId/unsuspend')
  unsuspendService(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
  ) {
    return this.whmcs.unsuspendService(
      request.whmcsInstallation!,
      externalServiceId,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Post('services/:externalServiceId/terminate')
  terminateService(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
  ) {
    return this.whmcs.terminateService(
      request.whmcsInstallation!,
      externalServiceId,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Patch('services/:externalServiceId/change-package')
  changePackage(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
    @Body() body: unknown,
  ) {
    const parsed = whmcsChangePackageRequestSchema.parse(body);
    return this.whmcs.changePackage(
      request.whmcsInstallation!,
      externalServiceId,
      parsed,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Post('services/:externalServiceId/sso')
  createServiceSso(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
    @Body() body: unknown,
  ) {
    const parsed = whmcsCreateSsoRequestSchema.parse(body);
    return this.sso.createWhmcsServiceSso(
      request.whmcsInstallation!,
      externalServiceId,
      parsed,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Get('accounts')
  listAccounts(@Req() request: WhmcsAuthenticatedRequest) {
    return this.reconciliation.listAccounts(request.whmcsInstallation!);
  }

  @Post('services/import')
  importService(@Req() request: WhmcsAuthenticatedRequest, @Body() body: unknown) {
    const parsed = whmcsImportServiceRequestSchema.parse(body);
    return this.reconciliation.importService(
      request.whmcsInstallation!,
      parsed,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }

  @Post('reconcile')
  reconcileAll(@Req() request: WhmcsAuthenticatedRequest, @Body() body: unknown) {
    const parsed = whmcsReconcileRequestSchema.parse(body ?? {});
    return this.reconciliation.reconcileInstallation(
      request.whmcsInstallation!,
      parsed,
    );
  }

  @Post('services/:externalServiceId/reconcile')
  reconcileService(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
    @Body() body: unknown,
  ) {
    const parsed = whmcsReconcileRequestSchema.parse(body ?? {});
    return this.reconciliation.reconcileService(
      request.whmcsInstallation!,
      externalServiceId,
      parsed,
    );
  }

  @Get('events')
  listEvents(
    @Req() request: WhmcsAuthenticatedRequest,
    @Query('cursor') cursor?: string,
    @Query('limit') limit?: string,
  ) {
    const parsedLimit = limit ? Number.parseInt(limit, 10) : 50;
    return this.events.listEvents(
      request.whmcsInstallation!,
      cursor,
      Number.isFinite(parsedLimit) ? parsedLimit : 50,
    );
  }

  @Post('events/ack')
  acknowledgeEvents(@Req() request: WhmcsAuthenticatedRequest, @Body() body: unknown) {
    const parsed = whmcsEventsAckRequestSchema.parse(body);
    return this.events.acknowledgeEvents(request.whmcsInstallation!, parsed);
  }

  @Get('services/:externalServiceId/usage')
  getServiceUsage(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
    @Query() query: unknown,
  ) {
    const parsed = whmcsUsageQuerySchema.parse(query);
    return this.usage.getServiceUsage(
      request.whmcsInstallation!,
      externalServiceId,
      parsed.periodStart,
      parsed.periodEnd,
      parsed.testMode,
    );
  }

  @Post('services/:externalServiceId/usage/adjustments')
  createUsageAdjustment(
    @Req() request: WhmcsAuthenticatedRequest,
    @Param('externalServiceId') externalServiceId: string,
    @Body() body: unknown,
  ) {
    const parsed = whmcsUsageAdjustmentRequestSchema.parse(body);
    return this.usage.createAdjustment(
      request.whmcsInstallation!,
      externalServiceId,
      parsed,
      headerValue(request, INTEGRATION_HEADERS.idempotencyKey)!,
    );
  }
}

function headerValue(request: WhmcsAuthenticatedRequest, header: string): string | undefined {
  const value = request.headers[header];
  return typeof value === 'string' ? value : undefined;
}
