import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  Param,
  ParseUUIDPipe,
  Patch,
  Post,
} from '@nestjs/common';
import { RequiresModule } from '../../common/access/requires-module.decorator';
import type { AuthenticatedUser } from '../../common/auth/authenticated-user';
import { CurrentUser } from '../../common/auth/current-user.decorator';
import { CommunityService } from './community.service';
import { CreateAnnouncementDto, UpdateAnnouncementDto } from './dto/announcement.dto';
import { CreateChannelDto, UpdateChannelDto } from './dto/channel.dto';
import { CreateChatDto, CreateMessageDto } from './dto/chat.dto';
import { CreateCommentDto } from './dto/comment.dto';
import { CreatePostDto, UpdatePostDto } from './dto/post.dto';
import {
  RemovePushSubscriptionDto,
  SavePushSubscriptionDto,
} from './dto/push-subscription.dto';

/**
 * Community & Research.
 *
 * `@RequiresModule('community')` on the controller is the floor: no account
 * without the entitlement reaches any route here. The difference between a
 * reader and an editor is decided in the service, because it depends on the
 * content as well as the level — a draft is invisible to a reader, an admin-only
 * channel is invisible to everyone but administrators.
 */
@RequiresModule('community')
@Controller('community')
export class CommunityController {
  constructor(private readonly community: CommunityService) {}

  @Get('bootstrap')
  bootstrap(@CurrentUser() user: AuthenticatedUser) {
    return this.community.bootstrap(user);
  }

  @Get('push/configuration')
  pushConfiguration() {
    return this.community.pushConfiguration();
  }

  @Post('push/subscriptions')
  @HttpCode(204)
  savePushSubscription(
    @CurrentUser() user: AuthenticatedUser,
    @Body() input: SavePushSubscriptionDto,
  ) {
    return this.community.savePushSubscription(user, input);
  }

  @Delete('push/subscriptions')
  @HttpCode(204)
  removePushSubscription(
    @CurrentUser() user: AuthenticatedUser,
    @Body() input: RemovePushSubscriptionDto,
  ) {
    return this.community.removePushSubscription(user, input.endpoint);
  }

  @Get('posts/:id')
  post(@CurrentUser() user: AuthenticatedUser, @Param('id', ParseUUIDPipe) id: string) {
    return this.community.post(user, id);
  }

  @Post('posts')
  createPost(@CurrentUser() user: AuthenticatedUser, @Body() input: CreatePostDto) {
    return this.community.createPost(user, input);
  }

  @Patch('posts/:id')
  updatePost(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id', ParseUUIDPipe) id: string,
    @Body() input: UpdatePostDto,
  ) {
    return this.community.updatePost(user, id, input);
  }

  @Delete('posts/:id')
  @HttpCode(204)
  deletePost(@CurrentUser() user: AuthenticatedUser, @Param('id', ParseUUIDPipe) id: string) {
    return this.community.deletePost(user, id);
  }

  @Post('posts/:id/like')
  like(@CurrentUser() user: AuthenticatedUser, @Param('id', ParseUUIDPipe) id: string) {
    return this.community.setLike(user, id, true);
  }

  @Delete('posts/:id/like')
  unlike(@CurrentUser() user: AuthenticatedUser, @Param('id', ParseUUIDPipe) id: string) {
    return this.community.setLike(user, id, false);
  }

  @Post('posts/:id/comments')
  addComment(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id', ParseUUIDPipe) id: string,
    @Body() input: CreateCommentDto,
  ) {
    return this.community.addComment(user, id, input);
  }

  @Delete('comments/:id')
  @HttpCode(204)
  deleteComment(@CurrentUser() user: AuthenticatedUser, @Param('id', ParseUUIDPipe) id: string) {
    return this.community.deleteComment(user, id);
  }

  @Get('chats/:id/messages')
  messages(@CurrentUser() user: AuthenticatedUser, @Param('id') id: string) {
    return this.community.messages(user, id);
  }

  @Post('chats/:id/messages')
  addMessage(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id') id: string,
    @Body() input: CreateMessageDto,
  ) {
    return this.community.addMessage(user, id, input);
  }

  @Post('chats')
  createChat(@CurrentUser() user: AuthenticatedUser, @Body() input: CreateChatDto) {
    return this.community.createChat(user, input);
  }

  @Post('channels')
  createChannel(@CurrentUser() user: AuthenticatedUser, @Body() input: CreateChannelDto) {
    return this.community.createChannel(user, input);
  }

  @Patch('channels/:id')
  updateChannel(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id') id: string,
    @Body() input: UpdateChannelDto,
  ) {
    return this.community.updateChannel(user, id, input);
  }

  @Delete('channels/:id')
  @HttpCode(204)
  deleteChannel(@CurrentUser() user: AuthenticatedUser, @Param('id') id: string) {
    return this.community.deleteChannel(user, id);
  }

  @Post('announcements')
  createAnnouncement(
    @CurrentUser() user: AuthenticatedUser,
    @Body() input: CreateAnnouncementDto,
  ) {
    return this.community.createAnnouncement(user, input);
  }

  @Patch('announcements/:id')
  updateAnnouncement(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id', ParseUUIDPipe) id: string,
    @Body() input: UpdateAnnouncementDto,
  ) {
    return this.community.updateAnnouncement(user, id, input);
  }

  @Delete('announcements/:id')
  @HttpCode(204)
  deleteAnnouncement(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id', ParseUUIDPipe) id: string,
  ) {
    return this.community.deleteAnnouncement(user, id);
  }

  @Post('announcements/:id/read')
  markAnnouncementRead(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id', ParseUUIDPipe) id: string,
  ) {
    return this.community.markAnnouncementRead(user, id);
  }

  @Patch('settings')
  updateSettings(
    @CurrentUser() user: AuthenticatedUser,
    @Body() input: Record<string, unknown>,
  ) {
    return this.community.updateSettings(user, input);
  }
}
