import { Body, Controller, Post } from '@nestjs/common';
import {
  ApiBadRequestResponse,
  ApiBearerAuth,
  ApiBody,
  ApiConflictResponse,
  ApiForbiddenResponse,
  ApiOperation,
  ApiResponse,
  ApiTags,
  ApiUnauthorizedResponse
} from '@nestjs/swagger';
import { UserRole } from '@prisma/client';
import { CurrentUser } from '../common/current-user.decorator';
import { RequestUser } from '../common/request-user';
import { ApiAuthThrottleError, ApiProtectedEndpoint, ApiValidationError, ESTATE_ID_EXAMPLE } from '../common/swagger-docs';
import { AuthService } from './auth.service';
import { AuthThrottle } from './auth-throttle.decorator';
import { LoginDto, RefreshTokenDto, SignupDto, VerifyOtpDto } from './dto/auth.dto';
import { Public } from './public.decorator';
import { RequireRole } from './require-role.decorator';

@ApiTags('Auth')
@Controller({ path: 'auth', version: '1' })
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login')
  @Public()
  @AuthThrottle()
  @ApiOperation({
    summary: 'Request OTP for login',
    description:
      'Starts OTP login for a registered and active user. Delivery is controlled by the super-admin OTP configuration: DEV, Termii, Africa\'s Talking, or a custom HTTP SMS endpoint.'
  })
  @ApiBody({
    type: LoginDto,
    examples: {
      superAdmin: { summary: 'Seeded super admin', value: { phone: '+2348000000001' } },
      salesAdmin: { summary: 'Seeded sales admin', value: { phone: '+2348000000005' } },
      estateAdmin: { summary: 'Seeded estate admin', value: { phone: '+2348000000002' } },
      resident: { summary: 'Seeded resident', value: { phone: '+2348000000003' } },
      guard: { summary: 'Seeded guard', value: { phone: '+2348000000004' } }
    }
  })
  @ApiResponse({
    status: 201,
    description: 'OTP sent successfully',
    schema: {
      example: {
        message: 'OTP sent',
        provider: 'DEV',
        providerPriority: ['DEV'],
        expiresInSeconds: 300,
        devOtp: '123456'
      }
    }
  })
  @ApiUnauthorizedResponse({
    description: 'Phone number does not exist, or the account is suspended or disabled.',
    schema: { example: { statusCode: 401, message: 'No account exists for this phone number', error: 'Unauthorized' } }
  })
  @ApiAuthThrottleError()
  @ApiValidationError()
  login(@Body() dto: LoginDto) {
    return this.authService.login(dto);
  }

  @Post('verify-otp')
  @Public()
  @AuthThrottle()
  @ApiOperation({
    summary: 'Verify OTP and receive tokens',
    description:
      'Verifies the OTP for the selected provider, rejects inactive accounts, stores a hashed refresh token, and returns access and refresh JWTs.'
  })
  @ApiBody({
    type: VerifyOtpDto,
    examples: {
      seededSuperAdmin: { summary: 'Seeded super admin login in DEV mode', value: { phone: '+2348000000001', otp: '123456' } }
    }
  })
  @ApiResponse({
    status: 201,
    description: 'OTP verified, tokens issued',
    schema: {
      example: {
        accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
        refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
      }
    }
  })
  @ApiUnauthorizedResponse({
    description: 'OTP is invalid, user does not exist, or account is inactive.',
    schema: { example: { statusCode: 401, message: 'Invalid OTP', error: 'Unauthorized' } }
  })
  @ApiAuthThrottleError()
  @ApiValidationError()
  verifyOtp(@Body() dto: VerifyOtpDto) {
    return this.authService.verifyOtp(dto);
  }

  @Post('refresh')
  @Public()
  @AuthThrottle()
  @ApiOperation({
    summary: 'Refresh access and refresh tokens',
    description:
      'Validates the refresh JWT and compares it against the bcrypt hash stored on the user before rotating both tokens.'
  })
  @ApiBody({ type: RefreshTokenDto })
  @ApiResponse({
    status: 201,
    description: 'New tokens issued',
    schema: {
      example: {
        accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
        refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
      }
    }
  })
  @ApiUnauthorizedResponse({
    description: 'Refresh token is invalid, expired, revoked, or account is inactive.',
    schema: { example: { statusCode: 401, message: 'Invalid refresh token', error: 'Unauthorized' } }
  })
  @ApiAuthThrottleError()
  @ApiValidationError()
  refresh(@Body() dto: RefreshTokenDto) {
    return this.authService.refresh(dto);
  }

  @Post('signup')
  @RequireRole(UserRole.SUPER_ADMIN, UserRole.ESTATE_ADMIN, UserRole.SECURITY_SUPERVISOR)
  @ApiProtectedEndpoint()
  @ApiBearerAuth('bearer')
  @ApiOperation({
    summary: 'Create a user account and role profile',
    description:
      'Creates users for supported roles using an authenticated admin or security supervisor. Residents get a resident profile; guards get a guard profile. Role creation is limited by the creator role.'
  })
  @ApiBody({
    type: SignupDto,
    examples: {
      resident: {
        summary: 'Create resident',
        value: { estateId: ESTATE_ID_EXAMPLE, name: 'Ade Okafor', phone: '+2348000000100', role: 'RESIDENT', unit: 'B14' }
      },
      guard: {
        summary: 'Create guard',
        value: {
          estateId: ESTATE_ID_EXAMPLE,
          name: 'Ade Okafor',
          phone: '+2348000000101',
          role: 'GUARD',
          deviceId: 'guard-device-010'
        }
      },
      supervisor: {
        summary: 'Create security supervisor',
        value: { estateId: ESTATE_ID_EXAMPLE, name: 'Security Supervisor', phone: '+2348000000102', role: 'SECURITY_SUPERVISOR' }
      },
      salesAdmin: {
        summary: 'Create sales admin',
        value: { name: 'Ade Okafor', phone: '+2348000000103', role: 'SALES_ADMIN' }
      }
    }
  })
  @ApiResponse({
    status: 201,
    description: 'User account created. Related resident or guard profile is included when applicable.',
    schema: {
      example: {
        user: {
          id: 'user-uuid',
          estateId: ESTATE_ID_EXAMPLE,
          phone: '+2348000000100',
          name: 'Ade Okafor',
          role: 'RESIDENT',
          status: 'ACTIVE'
        },
        resident: {
          id: 'resident-uuid',
          estateId: ESTATE_ID_EXAMPLE,
          unit: 'B14'
        }
      }
    }
  })
  @ApiBadRequestResponse({
    description: 'Missing role-specific fields such as unit for RESIDENT, or validation failure.',
    schema: { example: { statusCode: 400, message: 'unit is required when signing up a resident', error: 'Bad Request' } }
  })
  @ApiConflictResponse({
    description: 'Phone number already belongs to an existing user.',
    schema: { example: { statusCode: 409, message: 'A user with this phone number already exists', error: 'Conflict' } }
  })
  @ApiForbiddenResponse({
    description: 'Creator role cannot create the requested role or estate scope is invalid.',
    schema: { example: { statusCode: 403, message: 'You cannot create users with role SUPER_ADMIN', error: 'Forbidden' } }
  })
  signup(@Body() dto: SignupDto, @CurrentUser() user: RequestUser) {
    return this.authService.signup(dto, user);
  }
}
