import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  Request,
  Res,
  UseGuards,
} from '@nestjs/common';
import type { Response, Request as ExpressRequest } from 'express';
import { SkipTransform } from '@common/decorators/skip-transform.decorator';
import { MeetingUsersService } from '../services/users.service';
import { handleMeetingAttendanceError } from '../errors/handle-controller-error';
import { getMeetingRoleFromUser, isMeetingAdminRole } from '../utils/meeting-roles';

@Controller('meeting-attendance/users')
@SkipTransform()
export class MeetingAttendanceUsersController {
  constructor(private readonly usersService: MeetingUsersService) {}

  @Get()
  async listUsers(
    @Query() query: Record<string, any>,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireAdminOrManager(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.listUsers(query as any);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Post()
  async createUser(
    @Body() body: Record<string, any>,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireAdminOrManager(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.createUser(body, req, req.user as any);
      return res.status(201).json({ user: result.user, message: result.message });
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Get('search')
  async searchUsers(
    @Query('q') query: string,
    @Query('limit') limit: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireMeetingUser(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.searchUsers(query || '', limit || '20');
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Get('import')
  async downloadImportTemplate(@Request() req: ExpressRequest, @Res() res: Response) {
    try {
      const actor = await this.requireAdminOrManager(req, res);
      if (!actor) {
        return res;
      }

      const headers = ['email', 'role'];
      const sampleData = [
        'user1@example.com,Employee',
        'user2@example.com,MeetingManager',
        'user3@example.com,Administrator',
      ];

      const csvContent = [
        headers.join(','),
        '# Sample data (please delete this row and below sample data)',
        '# Role options: Administrator/MeetingManager/Leader/Employee (ADMIN/MeetingManager/LEADER/EMPLOYEE are also accepted; legacy MANAGER is also accepted)',
        ...sampleData,
      ].join('\n');

      res.setHeader('Content-Type', 'text/csv; charset=utf-8');
      res.setHeader('Content-Disposition', 'attachment; filename="user-import-template.csv"');
      return res.status(200).send(csvContent);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Post('import')
  async importUsers(
    @Body() body: Record<string, any>,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireAdminOrManager(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.importUsers(body as any, req, req.user as any);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Get(':id')
  async getUserDetails(
    @Param('id') id: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireMeetingUser(req, res);
      if (!actor) {
        return res;
      }

      if (actor.id !== id && !isMeetingAdminRole(actor.role)) {
        return res.status(403).json({ error: 'Insufficient permissions' });
      }

      const user = await this.usersService.getUserDetails(id);
      return res.status(200).json(user);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Put(':id')
  async updateUser(
    @Param('id') id: string,
    @Body() body: Record<string, any>,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireMeetingUser(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.updateUser(id, body, actor, req, req.user as any);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Delete(':id')
  async disableUser(
    @Param('id') id: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireMeetingUser(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.disableUser(id, actor, req, req.user as any);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Post(':id/reset-password')
  async resetPassword(
    @Param('id') id: string,
    @Body() body: Record<string, any>,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireMeetingUser(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.resetPassword();
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  @Delete(':id/permanent')
  async deleteUserPermanent(
    @Param('id') id: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = await this.requireMeetingUser(req, res);
      if (!actor) {
        return res;
      }

      const result = await this.usersService.permanentDeleteUser(id, actor);
      if ((result as any).error) {
        return res.status(400).json(result);
      }
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Server error');
    }
  }

  private async requireMeetingUser(req: ExpressRequest, res: Response) {
    const user = req.user as
      | { userId?: string; id?: string; email?: string; roles?: Array<{ role?: { code?: string } } | string> }
      | undefined;
    const userId = user?.userId ?? user?.id;
    if (!userId || !user?.email) {
      res.status(401).json({ error: 'Unauthorized access' });
      return null;
    }

    const role = getMeetingRoleFromUser(user);
    if (!role) {
      res.status(403).json({ error: 'Insufficient permissions' });
      return null;
    }

    return { id: userId, role, email: user.email };
  }

  private async requireAdminOrManager(req: ExpressRequest, res: Response) {
    const actor = await this.requireMeetingUser(req, res);
    if (!actor) {
      return null;
    }

    if (!isMeetingAdminRole(actor.role)) {
      res.status(403).json({ error: 'Insufficient permissions' });
      return null;
    }

    return actor;
  }
}
