import { Body, Controller, Get, Inject, Param, Post, Query, Res, UseGuards } from "@nestjs/common";
import type Redis from "ioredis";
import {
  CreateZoomMeetingRequest,
  DisconnectConnectedAccountRequest,
  ListExternalEventsQuery,
  OAuthAccountProvider,
  RewriteMessageRequest,
  SaveOpenAiKeyRequest,
  StartOAuthRequest,
} from "@slackwsh/contracts";
import * as messaging from "@slackwsh/messaging";
import { AuthGuard } from "../auth/auth.guard";
import { CurrentUser } from "../auth/current-user.decorator";
import type { RequestUser } from "../auth/auth.guard";
import { REDIS_PUBSUB } from "../common/redis-pubsub.provider";

@Controller("integrations")
export class IntegrationsController {
  constructor(@Inject(REDIS_PUBSUB) private readonly redis: Redis) {}

  @UseGuards(AuthGuard)
  @Get()
  async list(@CurrentUser() user: RequestUser) {
    return { integrations: await messaging.listConnectedAccountStatuses(user.userId) };
  }

  @UseGuards(AuthGuard)
  @Post("openai")
  async saveOpenAi(@CurrentUser() user: RequestUser, @Body() body: unknown) {
    const { apiKey } = SaveOpenAiKeyRequest.parse(body);
    return messaging.saveOpenAiKey(user.userId, apiKey);
  }

  @UseGuards(AuthGuard)
  @Post("disconnect")
  async disconnect(@CurrentUser() user: RequestUser, @Body() body: unknown) {
    const { provider } = DisconnectConnectedAccountRequest.parse(body);
    await messaging.disconnectIntegration(user.userId, provider);
    return { disconnected: true };
  }

  @UseGuards(AuthGuard)
  @Post("rewrite")
  async rewrite(@CurrentUser() user: RequestUser, @Body() body: unknown) {
    const input = RewriteMessageRequest.parse(body);
    return messaging.rewriteMessage(user.userId, input);
  }

  @UseGuards(AuthGuard)
  @Post("oauth/start")
  async startOAuth(@CurrentUser() user: RequestUser, @Body() body: unknown) {
    const input = StartOAuthRequest.parse(body);
    return messaging.startOAuth(this.redis, user.userId, input.provider, input.returnTo);
  }

  /** Browser redirect from the provider — no AuthGuard; state binds the user. */
  @Get("oauth/:provider/callback")
  async oauthCallback(
    @Param("provider") providerParam: string,
    @Query("code") code: string | undefined,
    @Query("state") state: string | undefined,
    @Query("error") error: string | undefined,
    @Res() reply: { redirect: (url: string, code?: number) => unknown },
  ) {
    const parsed = OAuthAccountProvider.safeParse(providerParam);
    if (!parsed.success) {
      return reply.redirect(messaging.oauthCallbackRedirect("/settings", false, "Unknown provider"), 302);
    }
    if (error || !code || !state) {
      return reply.redirect(
        messaging.oauthCallbackRedirect("/settings", false, error || "OAuth was cancelled"),
        302,
      );
    }
    try {
      const { returnTo } = await messaging.completeOAuth(this.redis, parsed.data, code, state);
      return reply.redirect(messaging.oauthCallbackRedirect(returnTo, true), 302);
    } catch (err) {
      const message = err instanceof Error ? err.message : "Could not connect account";
      return reply.redirect(messaging.oauthCallbackRedirect("/settings", false, message), 302);
    }
  }

  @UseGuards(AuthGuard)
  @Get("calendar/external")
  async externalEvents(@CurrentUser() user: RequestUser, @Query() query: unknown) {
    const { from, to } = ListExternalEventsQuery.parse(query);
    return { events: await messaging.listExternalCalendarEvents(user.userId, from, to) };
  }

  @UseGuards(AuthGuard)
  @Post("zoom/meetings")
  async zoomMeeting(@CurrentUser() user: RequestUser, @Body() body: unknown) {
    const input = CreateZoomMeetingRequest.parse(body);
    return messaging.createZoomMeeting(user.userId, input);
  }
}
