import { applyDecorators, Body, Controller, Get, Param, Patch, Post, Query, Req, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { AuthGuard } from 'src/auth/auth.guard';
import { RolesGuard } from 'src/guard/role.guard';
import { AgentApprovedGuard } from 'src/guard/agent-approved.guard';
import { Roles } from 'src/guard/decorators/role.decorator';
import { CommonService } from 'src/common/common.service';
import { UserType } from 'src/user/schema/users.schema';
import { AgentService } from './agent.service';
import { AgentListDto, InviteAgentDto, RejectAgentDto, SendOtpDto, SubmitKycDto, UpdateAgentDto, VerifyOtpDto } from './dto/agent.dto';

// Every route below is guarded by role: admin routes by SUPER_ADMIN / ADMIN_STAFF, agent-app routes by AGENT.
// Only the two login routes are public.
const AdminOnly = () => applyDecorators(
    ApiTags('Agents (admin)'),
    ApiBearerAuth('access_token'),
    UseGuards(AuthGuard, RolesGuard),
    Roles(UserType.SUPER_ADMIN, UserType.ADMIN_STAFF)
);

const AgentOnly = () => applyDecorators(
    ApiTags('Agent app'),
    ApiBearerAuth('access_token'),
    UseGuards(AuthGuard, RolesGuard),
    Roles(UserType.AGENT)
);

const ApprovedAgentOnly = () => applyDecorators(
    ApiTags('Agent app'),
    ApiBearerAuth('access_token'),
    UseGuards(AuthGuard, RolesGuard, AgentApprovedGuard),
    Roles(UserType.AGENT)
);

@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
@Controller()
export class AgentController {
    constructor(
        private readonly agentService: AgentService,
        private readonly common: CommonService
    ) { }

    // ================= Admin panel: /admin/agents =================

    @Post('admin/agents')
    @AdminOnly()
    @ApiOperation({ summary: 'Invite an agent/POSP by email. The email carries the login link; the response returns it too.' })
    @ApiBody({ type: InviteAgentDto })
    @ApiResponse({ status: 400, description: 'Mobile or email already registered, or invalid categories or region' })
    invite(@Req() req: any, @Body() dto: InviteAgentDto) {
        return this.agentService.invite(dto, req.user_data, this.common.getUserLanguage(req));
    }

    @Get('admin/agents')
    @AdminOnly()
    @ApiOperation({ summary: 'List agents (filter by status, region_id, category_id, search). status=PENDING covers invited, accepted and submitted.' })
    list(@Req() req: any, @Query() query: AgentListDto) {
        return this.agentService.list(query, this.common.getUserLanguage(req));
    }

    @Get('admin/agents/summary')
    @AdminOnly()
    @ApiOperation({ summary: 'Counts for the filter chips: all, pending, approved, rejected' })
    summary(@Req() req: any) {
        return this.agentService.summary(this.common.getUserLanguage(req));
    }

    @Get('admin/agents/:id')
    @AdminOnly()
    @ApiOperation({ summary: 'Agent profile, KYC documents and activity log' })
    @ApiResponse({ status: 404, description: 'Agent not found' })
    getById(@Req() req: any, @Param('id') id: string) {
        return this.agentService.getById(id, this.common.getUserLanguage(req));
    }

    @Patch('admin/agents/:id')
    @AdminOnly()
    @ApiOperation({ summary: 'Edit profile, region, assigned categories, or activate/deactivate' })
    @ApiBody({ type: UpdateAgentDto })
    update(@Req() req: any, @Param('id') id: string, @Body() dto: UpdateAgentDto) {
        return this.agentService.update(id, dto, req.user_data, this.common.getUserLanguage(req));
    }

    @Post('admin/agents/:id/approve')
    @AdminOnly()
    @ApiOperation({ summary: 'Approve an agent whose KYC is submitted; marks all documents approved and notifies the agent' })
    approve(@Req() req: any, @Param('id') id: string) {
        return this.agentService.approve(id, req.user_data, this.common.getUserLanguage(req));
    }

    @Post('admin/agents/:id/reject')
    @AdminOnly()
    @ApiOperation({ summary: 'Reject a submitted KYC with a mandatory note; the agent must resubmit' })
    @ApiBody({ type: RejectAgentDto })
    reject(@Req() req: any, @Param('id') id: string, @Body() dto: RejectAgentDto) {
        return this.agentService.reject(id, dto, req.user_data, this.common.getUserLanguage(req));
    }

    @Post('admin/agents/:id/reopen')
    @AdminOnly()
    @ApiOperation({ summary: 'Reconsider a rejected agent: puts the KYC back into review' })
    reopen(@Req() req: any, @Param('id') id: string) {
        return this.agentService.reopen(id, req.user_data, this.common.getUserLanguage(req));
    }

    @Post('admin/agents/:id/resend-invite')
    @AdminOnly()
    @ApiOperation({ summary: 'Send the invite again (invited, accepted or rejected agents; needs an email on file)' })
    resendInvite(@Req() req: any, @Param('id') id: string) {
        return this.agentService.resendInvite(id, req.user_data, this.common.getUserLanguage(req));
    }

    // ================= Agent app: /agent =================

    @Post('agent/auth/send-otp')
    @ApiTags('Agent app')
    @ApiOperation({ summary: 'Send a login OTP to an invited agent\'s mobile number (mocked as 1234 for now)' })
    @ApiBody({ type: SendOtpDto })
    @ApiResponse({ status: 404, description: 'This number has not been invited' })
    sendOtp(@Req() req: any, @Body() dto: SendOtpDto) {
        return this.agentService.sendOtp(dto, this.common.getUserLanguage(req));
    }

    @Post('agent/auth/verify-otp')
    @ApiTags('Agent app')
    @ApiOperation({ summary: 'Verify the OTP and log in. The response says which screen to open next.' })
    @ApiBody({ type: VerifyOtpDto })
    @ApiResponse({ status: 400, description: 'Invalid or expired OTP' })
    verifyOtp(@Req() req: any, @Body() dto: VerifyOtpDto) {
        return this.agentService.verifyOtp(dto, this.common.getUserLanguage(req));
    }

    @Post('agent/auth/logout')
    @AgentOnly()
    @ApiOperation({ summary: 'Log out' })
    logout(@Req() req: any) {
        return this.agentService.logout(req.user_data._id, this.common.getUserLanguage(req));
    }

    @Get('agent/me')
    @AgentOnly()
    @ApiOperation({ summary: 'My profile and document status: review_status, next_screen, rejection note, documents' })
    me(@Req() req: any) {
        return this.agentService.me(req.user_data._id, this.common.getUserLanguage(req));
    }

    @Post('agent/kyc')
    @AgentOnly()
    @ApiOperation({ summary: 'Submit personal details and the four documents, or resubmit after a rejection. Upload files first with POST /uploads/file.' })
    @ApiBody({ type: SubmitKycDto })
    @ApiResponse({ status: 400, description: 'Already under review or approved, missing documents, or invalid PAN' })
    submitKyc(@Req() req: any, @Body() dto: SubmitKycDto) {
        return this.agentService.submitKyc(req.user_data._id, dto, this.common.getUserLanguage(req));
    }

    @Get('agent/dashboard')
    @ApprovedAgentOnly()
    @ApiOperation({ summary: 'Dashboard (placeholder). Only approved agents get in; others receive 403.' })
    @ApiResponse({ status: 403, description: 'Documents not approved yet' })
    dashboard(@Req() req: any) {
        return this.agentService.dashboard(req.user_data._id, this.common.getUserLanguage(req));
    }
}
