import {
  Body,
  Controller,
  Delete,
  Get,
  HttpStatus,
  Param,
  Patch,
  Post,
  Req,
  Res,
  UseGuards,
  UsePipes,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { ZodValidationPipe } from '../../common/pipes/zod-validation.pipe';
import { createGeneralError } from '../../utils';
import { JwtAuthGuard } from '../owner/guards/jwt.guard';
import {
  createAccessRoleDto,
  createAccessRoleSchema,
  updateAccessRoleDto,
  updateAccessRoleSchema,
} from './access-control.dto';
import { AccessControlService } from './access-control.service';
import { PermissionUserContext } from '../../common/access-control/permission-context.types';

@Controller('access-control')
export class AccessControlController {
  constructor(private readonly service: AccessControlService) {}

  private assertOwner(req: Request & { user?: { role?: string } }) {
    if (req.user?.role !== 'owner') {
      throw createGeneralError(
        'Only the organization owner can manage access control',
        'role',
        HttpStatus.FORBIDDEN,
      );
    }
  }

  private assertOwnerOrAdmin(req: Request & { user?: { role?: string } }) {
    if (!['owner', 'admin'].includes(req.user?.role ?? '')) {
      throw createGeneralError(
        'You do not have permission to view access roles',
        'role',
        HttpStatus.FORBIDDEN,
      );
    }
  }

  @UseGuards(JwtAuthGuard)
  @Get('me/permissions')
  async getMyPermissions(
    @Req() req: Request & { user?: PermissionUserContext },
    @Res() res: Response,
  ) {
    const user = req.user;

    if (!user?.sub || !user?.owner_id) {
      throw createGeneralError(
        'Unauthorized',
        'auth',
        HttpStatus.UNAUTHORIZED,
      );
    }

    const result = await this.service.getMyPermissions({
      sub: user.sub,
      owner_id: user.owner_id,
      role: user.role ?? 'member',
      access_role_id: user.access_role_id ?? null,
    });

    return res.status(HttpStatus.OK).json(result);
  }

  @UseGuards(JwtAuthGuard)
  @Get('permissions')
  async getPermissionCatalog(
    @Req() req: Request & { user?: { role?: string } },
    @Res() res: Response,
  ) {
    this.assertOwner(req);
    const result = await this.service.getPermissionCatalog();
    return res.status(HttpStatus.OK).json(result);
  }

  @UseGuards(JwtAuthGuard)
  @Get('roles')
  async getRoles(
    @CurrentUser() owner_id: string,
    @Req() req: Request & { user?: { role?: string } },
    @Res() res: Response,
  ) {
    this.assertOwnerOrAdmin(req);
    const result = await this.service.getRoles(owner_id);
    return res.status(HttpStatus.OK).json(result);
  }

  @UseGuards(JwtAuthGuard)
  @Get('roles/:roleId')
  async getRoleById(
    @Param('roleId') roleId: string,
    @CurrentUser() owner_id: string,
    @Req() req: Request & { user?: { role?: string } },
    @Res() res: Response,
  ) {
    this.assertOwner(req);
    const result = await this.service.getRoleById(roleId, owner_id);
    return res.status(HttpStatus.OK).json(result);
  }

  @UseGuards(JwtAuthGuard)
  @UsePipes(new ZodValidationPipe(createAccessRoleSchema))
  @Post('roles')
  async createRole(
    @Body() data: createAccessRoleDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request & { user?: { role?: string } },
    @Res() res: Response,
  ) {
    this.assertOwner(req);
    const result = await this.service.createRole(data, owner_id);
    return res.status(HttpStatus.CREATED).json(result);
  }

  @UseGuards(JwtAuthGuard)
  @UsePipes(new ZodValidationPipe(updateAccessRoleSchema))
  @Patch('roles/:roleId')
  async updateRole(
    @Param('roleId') roleId: string,
    @Body() data: updateAccessRoleDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request & { user?: { role?: string } },
    @Res() res: Response,
  ) {
    this.assertOwner(req);
    const result = await this.service.updateRole(roleId, data, owner_id);
    return res.status(HttpStatus.OK).json(result);
  }

  @UseGuards(JwtAuthGuard)
  @Delete('roles/:roleId')
  async deleteRole(
    @Param('roleId') roleId: string,
    @CurrentUser() owner_id: string,
    @Req() req: Request & { user?: { role?: string } },
    @Res() res: Response,
  ) {
    this.assertOwner(req);
    const result = await this.service.deleteRole(roleId, owner_id);
    return res.status(HttpStatus.OK).json(result);
  }
}
