import {
  Controller,
  Get,
  Query,
  Logger,
  InternalServerErrorException,
  Post,
  Body,
  UseInterceptors,
  UploadedFile,
  Req,
  HttpException,
  HttpStatus,
  BadRequestException,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiQuery, ApiBody } from '@nestjs/swagger';
import { Public } from '../../../modules/auth/public.decorator';
import { DashboardService } from '../../../modules/dashboard/dashboard.service';
import { DataSource } from 'typeorm';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
import { Request } from 'express';

@ApiTags('mobile_applied_jobs')
@Controller('mobile/seeker')
export class MobileAppliedJobsController {
  private readonly logger = new Logger(MobileAppliedJobsController.name);

  constructor(
    private readonly dashboardService: DashboardService,
    private readonly dataSource: DataSource,
  ) {}

  private getTimeAgoString(createdAt: any): string {
    const createdDate = createdAt ? new Date(createdAt) : null;
    const now = new Date();

    if (!createdDate || Number.isNaN(createdDate.getTime())) {
      return '';
    }

    const diffMs = now.getTime() - createdDate.getTime();
    if (diffMs < 0) return 'just now';

    const minutes = Math.floor(diffMs / (60 * 1000));
    if (minutes < 1) return 'just now';
    if (minutes < 60) return `${minutes} minutes ago`;

    const hours = Math.floor(minutes / 60);
    if (hours < 24) return `${hours} hours ago`;

    const days = Math.floor(hours / 24);
    if (days < 30) return `${days} days ago`;

    const months = Math.floor(days / 30);
    if (months < 12) return `${months} months ago`;

    const years = Math.floor(months / 12);
    return `${years} years ago`;
  }

  private formatAppliedAt(appliedAt: any): string {
    const date = appliedAt ? new Date(appliedAt) : null;
    if (!date || Number.isNaN(date.getTime())) {
      return '';
    }
    const months = [
      'January', 'February', 'March', 'April', 'May', 'June',
      'July', 'August', 'September', 'October', 'November', 'December'
    ];
    const month = months[date.getMonth()];
    const day = String(date.getDate()).padStart(2, '0');
    const year = date.getFullYear();
    return `Applied ${month} ${day}, ${year}`;
  }

  @Public()
  @Get('applied_jobs')
  @ApiOperation({
    summary: 'Get aggregated applied jobs list and stats for mobile app',
  })
  @ApiQuery({
    name: 'userId',
    required: true,
    type: String,
    description: 'User ID',
  })
  @ApiQuery({
    name: 'page',
    required: false,
    type: Number,
    description: 'Page number (default: 1)',
  })
  @ApiQuery({
    name: 'limit',
    required: false,
    type: Number,
    description: 'Items per page (default: 10)',
  })
  @ApiQuery({
    name: 'status',
    required: false,
    type: String,
    description: 'Filter by application status',
  })
  async getMobileAppliedJobs(
    @Query('userId') userId: string,
    @Query('page') page?: number,
    @Query('limit') limit?: number,
    @Query('status') status?: string,
  ) {
    if (!userId) {
      return {
        success: false,
        message: 'userId is required',
        stats: {
          total: 0,
          expired: 0,
          active: 0,
          applied: 0,
          shortlisted: 0,
          interview: 0,
          hired: 0,
          rejected: 0,
        },
        applications: [],
        pagination: {
          page: 1,
          limit: 10,
          total: 0,
          totalPages: 0,
        },
      };
    }

    this.logger.log(
      `Mobile applied jobs request - userId: ${userId}, page: ${page}, limit: ${limit}, status: ${status}`,
    );

    // Set defaults
    const pageNum = page ? Number(page) : 1;
    const limitNum = limit ? Number(limit) : 10;

    // Initialize default response structure
    const response = {
      success: true,
      stats: {
        total: 0,
        expired: 0,
        active: 0,
        applied: 0,
        shortlisted: 0,
        interview: 0,
        hired: 0,
        rejected: 0,
      },
      applications: [] as any[],
      pagination: {
        page: pageNum,
        limit: limitNum,
        total: 0,
        totalPages: 0,
      },
    };

    // 1. Fetch applied jobs list (independent call with error handling)
    try {
      const appliedJobsResult =
        await this.dashboardService.getSeekerAppliedJobs(userId, {
          page: pageNum,
          limit: limitNum,
          status: status,
        });

      if (
        appliedJobsResult &&
        appliedJobsResult.success &&
        appliedJobsResult.data
      ) {
        let apps = appliedJobsResult.data.applications || [];
        if (apps.length > 0) {
          apps.forEach((app: any) => {
            app.applied_at = this.formatAppliedAt(app.applied_at);
          });
          const jobIds = apps.map((app: any) => app.job_post_id).filter(Boolean);
          if (jobIds.length > 0) {
            const jobDataRaw = await this.dataSource.query(
              `SELECT jp.id as job_post_id, jp.created_at, s.skill_name as skill_name 
               FROM job_posts jp
               LEFT JOIN job_post_skills jps ON jps.job_post_id = jp.id 
               LEFT JOIN skills s ON jps.skill_id = s.id 
               WHERE jp.id IN (?)`,
              [jobIds],
            );
            const skillsByJob = jobDataRaw.reduce((acc: any, curr: any) => {
              if (!acc[curr.job_post_id]) {
                acc[curr.job_post_id] = { skills: [], created_at: curr.created_at };
              }
              if (curr.skill_name) {
                acc[curr.job_post_id].skills.push(curr.skill_name);
              }
              return acc;
            }, {});
            apps.forEach((app: any) => {
              const jobData = skillsByJob[app.job_post_id] || { skills: [], created_at: null };
              app.required_skills = jobData.skills;
              app.posted_ago = this.getTimeAgoString(jobData.created_at);
              delete app.skills;
            });
          }
        }
        response.applications = apps;
        if (appliedJobsResult.data.pagination) {
          response.pagination = {
            page: appliedJobsResult.data.pagination.page || pageNum,
            limit: appliedJobsResult.data.pagination.limit || limitNum,
            total: appliedJobsResult.data.pagination.total || 0,
            totalPages: appliedJobsResult.data.pagination.totalPages || 0,
          };
        }
      } else {
        this.logger.warn(
          `Applied jobs list returned unsuccessful for userId ${userId}`,
        );
      }
    } catch (error) {
      this.logger.error(
        `Failed to fetch applied jobs list for userId ${userId}`,
        error instanceof Error ? error.stack : String(error),
      );

      throw new InternalServerErrorException(
        'Unable to fetch applied jobs list',
      );
    }

    // 2. Fetch applied jobs stats (independent call with error handling)
    try {
      const statsResult =
        await this.dashboardService.getSeekerAppliedJobsStats(userId);

      if (statsResult && statsResult.success && statsResult.data) {
        response.stats = {
          total: statsResult.data.total || 0,
          expired: statsResult.data.expired || 0,
          active: statsResult.data.active || 0,
          applied: statsResult.data.applied || 0,
          shortlisted: statsResult.data.shortlisted || 0,
          interview: statsResult.data.interview || 0,
          hired: statsResult.data.hired || 0,
          rejected: statsResult.data.rejected || 0,
        };
      } else {
        this.logger.warn(
          `Applied jobs stats returned unsuccessful for userId ${userId}`,
        );
      }
    } catch (error) {
      this.logger.error(
        `Failed to fetch applied jobs stats for userId ${userId}`,
        error instanceof Error ? error.stack : String(error),
      );
      // Continue with default stats
    }

    return response;
  }

  @Public()
  @Post('applied_jobs/upload_resume')
  @ApiOperation({ summary: 'Upload Resume for Job Application (mobile)' })
  @UseInterceptors(
    FileInterceptor('resume', {
      storage: diskStorage({
        destination: './uploads/resumes',
        filename: (req, file, cb) => {
          const uniqueSuffix =
            Date.now() + '-' + Math.round(Math.random() * 1e9);
          cb(null, uniqueSuffix + extname(file.originalname));
        },
      }),
      limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit
      fileFilter: (req, file, cb) => {
        if (!file.originalname.match(/\.(pdf|doc|docx)$/i)) {
          return cb(new BadRequestException('Only document files are allowed!'), false);
        }
        cb(null, true);
      },
    }),
  )
  async uploadResumeForApply(
    @UploadedFile() file: Express.Multer.File,
    @Body('userId') userIdRaw?: string,
    @Body('seekerId') seekerIdRaw?: string,
    @Body('name') name?: string,
    @Req() req?: Request,
  ) {
    if (!file) {
      throw new BadRequestException('Resume file is required');
    }

    let seekerId: number | null = null;

    if (seekerIdRaw) {
      seekerId = Number(seekerIdRaw);
    } else if (userIdRaw) {
      const resolvedSeekerId = await this.dashboardService.getSeekerIdByUserId(Number(userIdRaw));
      if (resolvedSeekerId) {
        seekerId = resolvedSeekerId;
      }
    }

    if (!seekerId) {
      throw new BadRequestException('Valid userId or seekerId is required');
    }

    try {
      const result = await this.dashboardService.uploadResume(
        seekerId,
        file,
        name,
        req as any,
      );

      return result;
    } catch (error: any) {
      throw new HttpException(
        error.message || 'Failed to upload resume',
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  @Public()
  @Post('applied_jobs/apply')
  @ApiOperation({ summary: 'Apply for Job (mobile)' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        userId: { type: 'string', example: '123' },
        jobPostId: { type: 'string', example: '456' },
        resumeId: { type: 'number', example: 789 },
      },
      required: ['userId', 'jobPostId'],
    },
  })
  async applyForJob(
    @Body()
    body: {
      userId: string;
      jobPostId: string;
      resumeId?: string;
    },
  ) {
    if (!body) {
      throw new BadRequestException('Request body is required');
    }
    const { userId, jobPostId, resumeId } = body;
    if (!userId || !jobPostId) {
      throw new BadRequestException('userId and jobPostId are required');
    }

    let finalResumeId = resumeId;

    if (!finalResumeId) {
      // Check if user is blue_collar or white_collar
      const userResult = await this.dataSource.query(
        `SELECT selected_role_category FROM users WHERE id = ?`,
        [Number(userId)],
      );
      const isBlueCollar =
        userResult && userResult[0]?.selected_role_category === 'blue_collar';

      if (!isBlueCollar) {
        const resumesResult = await this.dashboardService.getSeekerResumesByUserId(Number(userId));
        if (
          resumesResult?.success &&
          Array.isArray(resumesResult.resumes) &&
          resumesResult.resumes.length > 0
        ) {
          // Sort descending by id to get the latest uploaded one
          const sorted = [...resumesResult.resumes].sort((a: any, b: any) => b.id - a.id);
          finalResumeId = String(sorted[0].id);
        } else {
          return {
            success: false,
            code: 'RESUME_REQUIRED',
            message: 'No resume found. Please upload a resume first.',
          };
        }
      }
    }

    const result = await this.dashboardService.applyForJob(
      userId,
      jobPostId,
      undefined,
      finalResumeId,
    );

    return result;
  }
}
