import { Controller, Get, Post, Delete, Body, UseGuards, Query, Param, Req } from '@nestjs/common';
import type { Request } from 'express';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { AuthService } from './auth.service';
import { InvitesService } from './invites.service';
import { SystemConfigService } from '../../shared/config/system-config.service';
import { LoginDto, ChangePasswordDto, RegisterDto, GenerateInviteDto, ForgotPasswordDto } from './auth.dto';
import { Public, CurrentUser } from '../../shared/common/decorators';
import { JwtAuthGuard } from './guards';
import { jsonResponse } from '../../shared/common/filters';
import { getClientIp } from '../../shared/common/client-ip.util';

import { PrismaService } from '../../shared/prisma/prisma.service';

@ApiTags('Auth')
@Controller()
export class AuthController {
  constructor(
    private auth: AuthService,
    private invites: InvitesService,
    private systemConfig: SystemConfigService,
    private prisma: PrismaService,
  ) {}

  @Public()
  @Post('player/auth/login')
  async playerLogin(@Body() dto: LoginDto) {
    const result = await this.auth.login(
      dto.username,
      dto.password,
      'player',
      dto.countryCode,
    );
    return jsonResponse(result);
  }

  @Public()
  @Post('player/auth/register')
  async playerRegister(@Body() dto: RegisterDto) {
    const result = await this.auth.registerPlayer({
      username: dto.username,
      phone: dto.phone,
      countryCode: dto.countryCode,
      password: dto.password,
      smsCode: dto.smsCode,
      sessionId: dto.sessionId,
      inviteCode: dto.inviteCode,
      locale: dto.locale,
    });
    return jsonResponse(result);
  }

  @Public()
  @Post('player/auth/forgot-password')
  async playerForgotPassword(@Body() dto: ForgotPasswordDto, @Req() req: Request) {
    const result = await this.auth.resetPasswordByPhone({
      phone: dto.phone,
      countryCode: dto.countryCode,
      smsCode: dto.smsCode,
      sessionId: dto.sessionId,
      newPassword: dto.newPassword,
      ipAddress: getClientIp(req),
    });
    return jsonResponse(result);
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Get('manage/invite')
  async manageInvite(@CurrentUser('id') userId: bigint) {
    const info = await this.auth.getInviteInfo(userId);
    return jsonResponse(info);
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Post('manage/invite/generate')
  async manageInviteGenerate(
    @CurrentUser('id') userId: bigint,
    @CurrentUser('userType') userType: string,
    @Body() dto: GenerateInviteDto,
  ) {
    const info = await this.auth.generateInviteCode(userId, userType, dto.cashbackRate);
    return jsonResponse(info);
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Get('manage/invites')
  async manageInvites(
    @CurrentUser('id') userId: bigint,
    @CurrentUser('userType') userType: string,
    @Query('page') page?: string,
    @Query('pageSize') pageSize?: string,
    @Query('status') status?: string,
    @Query('sponsorId') sponsorId?: string,
    @Query('keyword') keyword?: string,
  ) {
    const result = await this.invites.listInvites(
      { id: userId, userType },
      {
        page: page ? parseInt(page, 10) : 1,
        pageSize: pageSize ? parseInt(pageSize, 10) : 20,
        status,
        sponsorId,
        keyword,
      },
    );
    return jsonResponse(result);
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Get('manage/invites/sponsors')
  async manageInviteSponsors(
    @CurrentUser('id') userId: bigint,
    @CurrentUser('userType') userType: string,
  ) {
    const items = await this.invites.listSponsorOptions({ id: userId, userType });
    return jsonResponse({ items });
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Post('manage/invites/:id/revoke')
  async manageInviteRevoke(
    @CurrentUser('id') userId: bigint,
    @CurrentUser('userType') userType: string,
    @Param('id') id: string,
  ) {
    const item = await this.invites.revokeInvite({ id: userId, userType }, BigInt(id));
    return jsonResponse(item);
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Delete('manage/invites/:id')
  async manageInviteDelete(
    @CurrentUser('id') userId: bigint,
    @CurrentUser('userType') userType: string,
    @Param('id') id: string,
  ) {
    const result = await this.invites.deleteInvite({ id: userId, userType }, BigInt(id));
    return jsonResponse(result);
  }

  @Public()
  @Post('admin/auth/login')
  async adminLogin(@Body() dto: LoginDto) {
    const result = await this.auth.login(dto.username, dto.password, 'admin');
    return jsonResponse(result);
  }

  @Public()
  @Post('agent/auth/login')
  async agentLogin(@Body() dto: LoginDto) {
    const result = await this.auth.login(dto.username, dto.password, 'agent');
    return jsonResponse(result);
  }

  @Public()
  @Post('manage/auth/login')
  async manageLogin(@Body() dto: LoginDto) {
    const result = await this.auth.staffLogin(dto.username, dto.password);
    return jsonResponse(result);
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Get('manage/auth/me')
  async manageMe(
    @CurrentUser('id') userId: bigint,
    @CurrentUser('username') username: string,
    @CurrentUser('userType') userType: string,
    @CurrentUser('locale') locale: string | undefined,
    @CurrentUser('role') role: string | undefined,
    @CurrentUser('permissions') permissions: string[] | undefined,
    @CurrentUser('agentLevel') agentLevel: number | null | undefined,
  ) {
    const level = userType === 'AGENT' ? agentLevel ?? null : null;
    let maxAgentLevel: number | null = null;
    let canManageSubAgents = false;
    if (userType === 'AGENT' && level != null && level > 0) {
      const hierarchy = await this.systemConfig.getAgentHierarchySettings();
      maxAgentLevel = hierarchy.maxAgentLevel;
      canManageSubAgents =
        maxAgentLevel === 0 || level < maxAgentLevel;
    }

    let inviteCode: string | null = null;
    if (userType === 'ADMIN' || userType === 'AGENT') {
      inviteCode = (await this.auth.getInviteInfo(userId)).inviteCode;
    }

    let visibleMenus: string | null = null;
    if (userType === 'ADMIN') {
      const userDb = await this.prisma.user.findUnique({
        where: { id: userId },
        select: { visibleMenus: true },
      });
      visibleMenus = userDb?.visibleMenus ?? null;
    }

    return jsonResponse({
      id: userId.toString(),
      username,
      userType,
      locale,
      role,
      permissions: userType === 'ADMIN' ? permissions ?? [] : undefined,
      agentLevel: level,
      maxAgentLevel,
      canManageSubAgents,
      inviteCode,
      visibleMenus,
    });
  }

  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @Post('player/auth/change-password')
  async playerChangePassword(
    @CurrentUser('id') userId: bigint,
    @Body() dto: ChangePasswordDto,
  ) {
    await this.auth.changePassword(userId, dto.oldPassword, dto.newPassword);
    return jsonResponse(null, 'Password changed');
  }
}
