import {
  BadRequestException,
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  Param,
  Patch,
  Post,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { AuthGuard } from '../common/auth/auth.guard.ts';
import { ControlPlaneGuard } from '../common/auth/control-plane.guard.ts';
import { TenancyInterceptor } from '../common/tenancy/tenancy.interceptor.ts';
import { AuditInterceptor } from '../common/audit/audit.interceptor.ts';
import { RoleGuard } from './role.guard.ts';
import { RequireRole } from './role.decorator.ts';
import { ConvService, type ChannelDto } from '../modules/conv/conv.service.ts';
import { WidgetAdminTools } from '../modules/conv/widget/widget.tools.ts';
import { EmailAdminTools } from '../modules/conv/email/email.tools.ts';
import { TwilioSmsAdminService } from '../modules/conv/twilio/twilio-sms-admin.service.ts';
import { MessageBirdSmsAdminService } from '../modules/conv/messagebird/messagebird-sms-admin.service.ts';
import { VapiAdminService } from '../modules/conv/vapi/vapi-admin.service.ts';
import { ThrellAdminService } from '../modules/conv/threll/threll-admin.service.ts';
import { ChannelAdminService } from '../modules/conv/channels/channel-admin.service.ts';
import {
  CreateWidgetBody,
  UpdateWidgetBody,
  SetupEmailBody,
  SendEmailTestBody,
  ConfigureTwilioSmsBody,
  SendTwilioSmsTestBody,
  ConfigureMessageBirdSmsBody,
  SendMessageBirdSmsTestBody,
  ConfigureVapiBody,
  VapiCallInitiateBody,
  ConfigureThrellBody,
  ChannelListOptionsBody,
  ThrellCallInitiateBody,
  ConfigureChannelBody,
  ChannelVoiceCallBody,
  ChannelSendTestBody,
} from '@getmunin/types';

interface ChannelListResponse {
  items: ChannelDto[];
}

@Controller('v1/conversations/channels')
@UseGuards(AuthGuard, ControlPlaneGuard, RoleGuard)
@UseInterceptors(TenancyInterceptor, AuditInterceptor)
@RequireRole('owner', 'admin')
export class ConvChannelsController {
  constructor(
    private readonly conv: ConvService,
    private readonly widgetTools: WidgetAdminTools,
    private readonly emailTools: EmailAdminTools,
    private readonly twilioSmsTools: TwilioSmsAdminService,
    private readonly messageBirdSmsTools: MessageBirdSmsAdminService,
    private readonly vapiTools: VapiAdminService,
    private readonly threllTools: ThrellAdminService,
    private readonly channelAdmin: ChannelAdminService,
  ) {}

  @Get('vendors')
  listVendors(): ReturnType<ChannelAdminService['listVendors']> {
    return this.channelAdmin.listVendors();
  }

  @Post()
  @HttpCode(200)
  async configureChannel(@Body() body: unknown): Promise<Awaited<ReturnType<ChannelAdminService['configure']>>> {
    const parsed = ConfigureChannelBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.channelAdmin.configure(parsed.data);
  }

  @Post(':id/test')
  @HttpCode(200)
  async testChannel(@Param('id') id: string): Promise<unknown> {
    return this.channelAdmin.test(id);
  }

  @Post(':id/call')
  @HttpCode(200)
  async callChannel(@Param('id') id: string, @Body() body: unknown): Promise<unknown> {
    const parsed = ChannelVoiceCallBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.channelAdmin.call({ channelId: id, to: parsed.data.to, customerName: parsed.data.customerName });
  }

  @Post(':id/send-test')
  @HttpCode(200)
  async sendTestChannel(@Param('id') id: string, @Body() body: unknown): Promise<unknown> {
    const parsed = ChannelSendTestBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.channelAdmin.sendTest({ channelId: id, to: parsed.data.to, body: parsed.data.body });
  }

  @Get()
  async list(): Promise<ChannelListResponse> {
    const items = await this.conv.listChannels();
    return { items };
  }

  @Post('widget')
  @HttpCode(201)
  async createWidget(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<WidgetAdminTools['createChannel']>>> {
    const parsed = CreateWidgetBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.widgetTools.createChannel(parsed.data);
  }

  @Patch('widget/:id')
  @HttpCode(200)
  async updateWidget(
    @Param('id') id: string,
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<WidgetAdminTools['updateChannel']>>> {
    const parsed = UpdateWidgetBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.widgetTools.updateChannel({ channelId: id, ...parsed.data });
  }

  @Post('widget/:id/rotate-key')
  @HttpCode(200)
  async rotateWidgetKey(@Param('id') id: string): Promise<{ widgetKey: string }> {
    return this.widgetTools.rotateKey({ channelId: id });
  }

  @Post('widget/:id/rotate-identity-secret')
  @HttpCode(200)
  async rotateIdentitySecret(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<WidgetAdminTools['rotateIdentitySecret']>>> {
    return this.widgetTools.rotateIdentitySecret({ channelId: id });
  }

  @Post('email')
  @HttpCode(200)
  async setupEmail(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<EmailAdminTools['setupChannel']>>> {
    const parsed = SetupEmailBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.emailTools.setupChannel(parsed.data);
  }

  @Post('email/:id/test')
  @HttpCode(200)
  async testEmail(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<EmailAdminTools['testChannel']>>> {
    return this.emailTools.testChannel({ channelId: id });
  }

  @Post('email/:id/send-test')
  @HttpCode(200)
  async sendTestEmail(
    @Param('id') id: string,
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<EmailAdminTools['sendTest']>>> {
    const parsed = SendEmailTestBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.emailTools.sendTest({ channelId: id, to: parsed.data.to });
  }

  @Post('twilio-sms')
  @HttpCode(200)
  async configureTwilioSms(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<TwilioSmsAdminService['configure']>>> {
    const parsed = ConfigureTwilioSmsBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.twilioSmsTools.configure(parsed.data);
  }

  @Post('twilio-sms/:id/test')
  @HttpCode(200)
  async testTwilioSms(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<TwilioSmsAdminService['testChannel']>>> {
    return this.twilioSmsTools.testChannel({ channelId: id });
  }

  @Post('twilio-sms/:id/send-test')
  @HttpCode(200)
  async sendTwilioSmsTest(
    @Param('id') id: string,
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<TwilioSmsAdminService['sendTest']>>> {
    const parsed = SendTwilioSmsTestBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.twilioSmsTools.sendTest({
      channelId: id,
      to: parsed.data.to,
      body: parsed.data.body,
    });
  }

  @Post('messagebird-sms')
  @HttpCode(200)
  async configureMessageBirdSms(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<MessageBirdSmsAdminService['configure']>>> {
    const parsed = ConfigureMessageBirdSmsBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.messageBirdSmsTools.configure(parsed.data);
  }

  @Post('messagebird-sms/:id/test')
  @HttpCode(200)
  async testMessageBirdSms(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<MessageBirdSmsAdminService['testChannel']>>> {
    return this.messageBirdSmsTools.testChannel({ channelId: id });
  }

  @Post('messagebird-sms/:id/send-test')
  @HttpCode(200)
  async sendMessageBirdSmsTest(
    @Param('id') id: string,
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<MessageBirdSmsAdminService['sendTest']>>> {
    const parsed = SendMessageBirdSmsTestBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.messageBirdSmsTools.sendTest({
      channelId: id,
      to: parsed.data.to,
      body: parsed.data.body,
    });
  }

  @Post('vapi')
  @HttpCode(200)
  async configureVapi(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<VapiAdminService['configure']>>> {
    const parsed = ConfigureVapiBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.vapiTools.configure(parsed.data);
  }

  @Post('vapi/:id/test')
  @HttpCode(200)
  async testVapi(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<VapiAdminService['testChannel']>>> {
    return this.vapiTools.testChannel({ channelId: id });
  }

  @Post('vapi/:id/call')
  @HttpCode(200)
  async vapiCall(
    @Param('id') id: string,
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<VapiAdminService['callInitiate']>>> {
    const parsed = VapiCallInitiateBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.vapiTools.callInitiate({
      channelId: id,
      to: parsed.data.to,
      customerName: parsed.data.customerName,
    });
  }

  @Post('threll')
  @HttpCode(200)
  async configureThrell(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<ThrellAdminService['configure']>>> {
    const parsed = ConfigureThrellBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.threllTools.configure(parsed.data);
  }

  @Post('options')
  @HttpCode(200)
  async listChannelOptions(
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<ChannelAdminService['listOptions']>>> {
    const parsed = ChannelListOptionsBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.channelAdmin.listOptions({ vendor: parsed.data.vendor, config: parsed.data.config });
  }

  @Post(':id/options')
  @HttpCode(200)
  async listChannelOptionsForChannel(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<ChannelAdminService['listOptions']>>> {
    return this.channelAdmin.listOptions({ channelId: id });
  }

  @Post('threll/:id/test')
  @HttpCode(200)
  async testThrell(
    @Param('id') id: string,
  ): Promise<Awaited<ReturnType<ThrellAdminService['testChannel']>>> {
    return this.threllTools.testChannel({ channelId: id });
  }

  @Post('threll/:id/call')
  @HttpCode(200)
  async threllCall(
    @Param('id') id: string,
    @Body() body: unknown,
  ): Promise<Awaited<ReturnType<ThrellAdminService['callInitiate']>>> {
    const parsed = ThrellCallInitiateBody.safeParse(body ?? {});
    if (!parsed.success) throw new BadRequestException(parsed.error.message);
    return this.threllTools.callInitiate({
      channelId: id,
      to: parsed.data.to,
      customerName: parsed.data.customerName,
    });
  }

  @Delete(':id')
  @HttpCode(204)
  async archive(@Param('id') id: string): Promise<void> {
    await this.channelAdmin.onArchive(id);
    await this.conv.archiveChannel(id);
  }

  @Post(':id/activate')
  @HttpCode(200)
  async activate(@Param('id') id: string): Promise<ChannelDto> {
    return this.conv.setChannelActive(id, true);
  }
}
