import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  Req,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { AssociatesService } from './associates.service';
import { Public } from '../auth/public.decorator';
import {
  ApiBearerAuth,
  ApiTags,
  ApiBody,
  ApiParam,
  ApiQuery,
  ApiOperation,
} from '@nestjs/swagger';

@ApiTags('associates')
@ApiBearerAuth('access-token')
@Controller('associates')
export class AssociatesController {
  constructor(private readonly associatesService: AssociatesService) {}

  // Admin: Get all associates with pagination
  @Public()
  @Get()
  @ApiOperation({ summary: 'Get All Associates' })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'status', required: false, type: String })
  async getAllAssociates(
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
  ) {
    try {
      return await this.associatesService.getAllAssociates(
        Number(page),
        Number(limit),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch associates',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Get associate by user ID (for logged-in associate's profile)
  @Public()
  @Get('user/:userId')
  async getAssociateByUserId(@Param('userId') userId: number) {
    try {
      return await this.associatesService.getAssociateByUserId(Number(userId));
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch associate',
        HttpStatus.NOT_FOUND,
      );
    }
  }

  // Admin: Get associate by ID
  @Public()
  @Get(':id')
  async getAssociateById(@Param('id') id: number) {
    try {
      return await this.associatesService.getAssociateById(Number(id));
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch associate',
        HttpStatus.NOT_FOUND,
      );
    }
  }

  // Admin: Create new associate
  @Public()
  @Post()
  @ApiOperation({ summary: 'Create Associate' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        full_name: { type: 'string', example: 'John Doe' },
        email: {
          type: 'string',
          format: 'email',
          example: 'associate@example.com',
        },
        username: { type: 'string', example: 'johndoe_assoc' },
        password: { type: 'string', example: 'securePassword123' },
        phone: { type: 'string', example: '+1234567890' },
        country_id: { type: 'number', example: 101 },
        state_id: { type: 'number', example: 10 },
        city_id: { type: 'number', example: 5 },
        area_id: { type: 'number', example: 20 },
        address: { type: 'string', example: '123 Business St' },
        pincode: { type: 'string', example: '110001' },
        latitude: { type: 'number', example: 28.6139 },
        longitude: { type: 'number', example: 77.209 },
        status: {
          type: 'number',
          example: 1,
          description: '1 for active, 0 for inactive',
        },
      },
      required: ['full_name', 'email', 'username', 'password', 'phone'],
    },
  })
  async createAssociate(@Body() payload: any, @Req() req: any) {
    try {
      // Get admin ID from request (assuming you have auth)
      const adminId = req.user?.id || 1; // Default to 1 for testing
      return await this.associatesService.createAssociate(payload, adminId);
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to create associate',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Admin: Update associate
  @Public()
  @Put(':id')
  @ApiOperation({ summary: 'Update Associate' })
  @ApiParam({ name: 'id', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        name: { type: 'string' },
        email: { type: 'string' },
        status: { type: 'string' },
      },
    },
  })
  async updateAssociate(@Param('id') id: number, @Body() payload: any) {
    try {
      return await this.associatesService.updateAssociate(Number(id), payload);
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to update associate',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Admin: Toggle associate status
  @Public()
  @Put(':id/toggle-status')
  async toggleAssociateStatus(@Param('id') id: number) {
    try {
      return await this.associatesService.toggleAssociateStatus(Number(id));
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to toggle associate status',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Admin: Share associate credentials via email
  @Public()
  @Post(':id/share-credentials')
  @ApiOperation({ summary: 'Share Associate Credentials' })
  @ApiParam({ name: 'id', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        email: { type: 'string', format: 'email' },
        message: { type: 'string' },
      },
      required: ['email'],
    },
  })
  async shareAssociateCredentials(
    @Param('id') id: number,
    @Body() body: { email: string; message?: string },
  ) {
    try {
      return await this.associatesService.shareAssociateCredentials(
        Number(id),
        body.email,
        body.message,
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to share credentials',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Admin: Delete associate (soft delete)
  @Public()
  @Delete(':id')
  async deleteAssociate(@Param('id') id: number) {
    try {
      return await this.associatesService.deleteAssociate(Number(id));
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to delete associate',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Admin: Get associate's job posts
  @Public()
  @Get(':id/job-posts')
  @ApiOperation({ summary: 'Get Associate Job Posts' })
  @ApiParam({ name: 'id', type: Number })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  async getAssociateJobPosts(
    @Param('id') id: number,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
  ) {
    try {
      return await this.associatesService.getAssociateJobPosts(
        Number(id),
        Number(page),
        Number(limit),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch job posts',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Admin: Get applications for associate's jobs
  @Public()
  @Get(':id/applications')
  @ApiOperation({ summary: 'Get Associate Applications' })
  @ApiParam({ name: 'id', type: Number })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  async getAssociateApplications(
    @Param('id') id: number,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
  ) {
    try {
      return await this.associatesService.getAssociateApplications(
        Number(id),
        Number(page),
        Number(limit),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch applications',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Create job post with dummy company
  @Public()
  @Post('create-job')
  @ApiOperation({ summary: 'Create Associate Job Post' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        userId: {
          type: 'number',
          description: 'User ID of the associate creating the job',
        },
        company_id: {
          type: 'number',
          description: 'ID of the dummy company used',
        },
        jobTitle: { type: 'string', example: 'Software Engineer' },
        title: { type: 'string', example: 'Software Engineer' },
        description: { type: 'string' },
        requirements: { type: 'string' },
        benefits: { type: 'string' },
        salary_min: { type: 'number' },
        salary_max: { type: 'number' },
        job_type_id: { type: 'number' },
        experience_id: { type: 'number' },
        education_id: { type: 'number' },
        role_levels_id: { type: 'number' },
        vacancies: { type: 'number' },
        skills: { type: 'array', items: { type: 'string' } },
        tags: { type: 'array', items: { type: 'string' } },
        country_id: { type: 'number' },
        state_id: { type: 'number' },
        city_id: { type: 'number' },
        area_id: { type: 'number' },
        pincode: { type: 'string' },
        work_mode: { type: 'string', example: 'Remote' },
        shift: { type: 'string', example: 'Day' },
        urgent_hiring: { type: 'boolean' },
        walk_in_interview: {
          type: 'string',
          example: 'yes',
          description: 'yes or no',
        },
        walkin_address: {
          type: 'string',
          description: 'Required if walk_in_interview is yes',
        },
        walkin_start_date: {
          type: 'string',
          format: 'date',
          description: 'Required if walk_in_interview is yes',
        },
        walkin_end_date: {
          type: 'string',
          format: 'date',
          description: 'Required if walk_in_interview is yes',
        },
        walkin_start_time: {
          type: 'string',
          example: '10:00',
          description: 'Optional',
        },
        walkin_end_time: {
          type: 'string',
          example: '17:00',
          description: 'Optional',
        },
        walkin_instructions: {
          type: 'string',
          description: 'Optional instructions for walk-in candidates',
        },

        // Communication & Notification
        communication_preference: {
          type: 'string',
          enum: ['myself', 'other_recruiter', 'no'],
        },
        other_recruiter_name: {
          type: 'string',
          description:
            'Required if communication_preference is other_recruiter',
        },
        other_recruiter_whatsapp: {
          type: 'string',
          description:
            'Required if communication_preference is other_recruiter',
        },
        other_recruiter_email: {
          type: 'string',
          description:
            'Required if communication_preference is other_recruiter',
        },
        notification_preference: {
          type: 'string',
          enum: ['yes', 'daily_summary'],
        },

        // AI Interview Fields
        ai_interview_enabled: { type: 'boolean', default: false },
        ai_question_count: { type: 'number', example: 5 },
        ai_difficulty: { type: 'string', enum: ['easy', 'medium', 'hard'] },
        ai_experience_level: {
          type: 'string',
          description: 'Experience level ID or range',
        },
        ai_window_days: {
          type: 'number',
          default: 7,
          description: 'Mapped from ai_validity_days in frontend',
        },
        ai_interview_duration: { type: 'number', default: 30 },
        ai_generation_timing: {
          type: 'string',
          enum: ['apply', 'start'],
          default: 'apply',
        },
        ai_start_time: { type: 'string', example: '09:00' },
        ai_end_time: { type: 'string', example: '18:00' },
        ai_schedule_requires_approval: { type: 'boolean', default: false },
        ai_config: {
          type: 'array',
          items: {
            type: 'object',
            properties: {
              skill: { type: 'string' },
              count: { type: 'number' },
            },
          },
          description: 'Distribution of questions per skill',
        },

        application_deadline: { type: 'string', format: 'date-time' },
        status: {
          type: 'string',
          enum: ['pending', 'open'],
          default: 'pending',
        },
      },
      required: ['userId', 'company_id', 'title', 'description'],
    },
  })
  async createJobPost(@Body() payload: any) {
    try {
      return await this.associatesService.createAssociateJobPost(payload);
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to create job post',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Get own job posts by user_id
  @Public()
  @Get('user/:userId/job-posts')
  @ApiOperation({ summary: 'Get Job Posts by User ID' })
  @ApiParam({ name: 'userId', type: Number })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'search', required: false, type: String })
  @ApiQuery({ name: 'status', required: false, type: String })
  @ApiQuery({ name: 'hasApplications', required: false, type: Boolean })
  async getJobPostsByUserId(
    @Param('userId') userId: number,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
    @Query('search') search?: string,
    @Query('status') status?: string,
    @Query('hasApplications') hasApplications?: string,
  ) {
    try {
      return await this.associatesService.getJobPostsByUserId(
        Number(userId),
        Number(page),
        Number(limit),
        search,
        status,
        hasApplications,
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch job posts',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Get own applications by user_id
  @Public()
  @Get('user/:userId/applications')
  async getApplicationsByUserId(
    @Param('userId') userId: number,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
  ) {
    try {
      return await this.associatesService.getApplicationsByUserId(
        Number(userId),
        Number(page),
        Number(limit),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch applications',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Get applications with full candidate details
  @Public()
  @Get('user/:userId/applications-with-details')
  @ApiOperation({ summary: 'Get Applications with Details' })
  @ApiParam({ name: 'userId', type: Number })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'status', required: false, type: String })
  @ApiQuery({ name: 'jobId', required: false, type: Number })
  async getApplicationsWithDetails(
    @Param('userId') userId: number,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
    @Query('status') status?: string,
    @Query('jobId') jobId?: number,
  ) {
    try {
      return await this.associatesService.getApplicationsWithCandidateDetails(
        Number(userId),
        Number(page),
        Number(limit),
        status,
        jobId ? Number(jobId) : undefined,
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch applications',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Update application status
  @Public()
  @Put('user/:userId/applications/:applicationId/status')
  @ApiOperation({ summary: 'Update Application Status' })
  @ApiParam({ name: 'userId', type: Number })
  @ApiParam({ name: 'applicationId', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        status: {
          type: 'string',
          enum: ['shortlisted', 'rejected', 'interview', 'hired'],
        },
      },
      required: ['status'],
    },
  })
  async updateApplicationStatus(
    @Param('userId') userId: number,
    @Param('applicationId') applicationId: number,
    @Body() body: { status: string },
  ) {
    try {
      return await this.associatesService.updateApplicationStatusByUserId(
        Number(userId),
        Number(applicationId),
        body.status,
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to update application status',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Get application statistics
  @Public()
  @Get('user/:userId/application-stats')
  async getApplicationStats(@Param('userId') userId: number) {
    try {
      return await this.associatesService.getApplicationStatsByUserId(
        Number(userId),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch application stats',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Get saved candidates
  @Public()
  @Get('user/:userId/saved-candidates')
  async getSavedCandidates(
    @Param('userId') userId: number,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 10,
  ) {
    try {
      return await this.associatesService.getSavedCandidatesByUserId(
        Number(userId),
        Number(page),
        Number(limit),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to fetch saved candidates',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Save candidate
  @Public()
  @Post('user/:userId/saved-candidates')
  @ApiOperation({ summary: 'Save Candidate' })
  @ApiParam({ name: 'userId', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        candidateId: { type: 'number' },
        notes: { type: 'string' },
      },
      required: ['candidateId'],
    },
  })
  async saveCandidate(
    @Param('userId') userId: number,
    @Body() body: { candidateId: number; notes?: string },
  ) {
    try {
      return await this.associatesService.saveCandidateByUserId(
        Number(userId),
        body.candidateId,
        body.notes,
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to save candidate',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Remove saved candidate
  @Public()
  @Delete('user/:userId/saved-candidates/:candidateId')
  async removeSavedCandidate(
    @Param('userId') userId: number,
    @Param('candidateId') candidateId: number,
  ) {
    try {
      return await this.associatesService.removeSavedCandidateByUserId(
        Number(userId),
        Number(candidateId),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to remove saved candidate',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Check if candidate is saved
  @Public()
  @Get('user/:userId/saved-candidates/check/:candidateId')
  async checkSavedCandidate(
    @Param('userId') userId: number,
    @Param('candidateId') candidateId: number,
  ) {
    try {
      return await this.associatesService.checkSavedCandidateByUserId(
        Number(userId),
        Number(candidateId),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to check saved candidate',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // Associate: Promote job
  @Public()
  @Post('jobs/:jobId/promote')
  @ApiOperation({ summary: 'Promote Job' })
  @ApiParam({ name: 'jobId', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: { userId: { type: 'number' } },
      required: ['userId'],
    },
  })
  async promoteJob(
    @Param('jobId') jobId: number,
    @Body() body: { userId: number },
  ) {
    try {
      return await this.associatesService.promoteAssociateJob(
        Number(body.userId),
        Number(jobId),
      );
    } catch (error) {
      throw new HttpException(
        error.message || 'Failed to promote job',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  // NOTE: CV usage endpoints moved to AssociateCvUsageController
  // See: backend/src/modules/associate_cv_usage/associate_cv_usage.controller.ts
}
