import {
  Controller,
  Get,
  Put,
  Post,
  Body,
  Query,
  HttpException,
  HttpStatus,
  UseInterceptors,
  UploadedFiles,
  Req,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiConsumes,
  ApiQuery,
  ApiBody,
} from '@nestjs/swagger';
import { UserService } from '../../modules/user/user.service';
import { UserEntity } from '../../modules/user/entities/user.entity';
import { Public } from '../../modules/auth/public.decorator';
import { InjectRepository } from '@nestjs/typeorm';
import { Roles } from '../../modules/roles/entities/roles.entity';
import { SeekerResume } from '../../modules/seeker_resumes/entities/seeker_resumes.entity';
import { In, Repository } from 'typeorm';
import { FileFieldsInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
import { UserSessionEntity } from '../../modules/user_sessions/entities/user_session.entity';
import { MobileOnboardingStateService } from './mobile-onboarding-state.service';
import { AreaService } from '../../modules/area/area.service';
import { EducationLevelsService } from '../../modules/education_levels/education_levels.service';

@ApiTags('Mobile_Onboarding')
@Controller('mobile/onboarding')
export class MobileOnboardingController {
  constructor(
    private readonly userService: UserService,
    private readonly mobileOnboardingStateService: MobileOnboardingStateService,
    @InjectRepository(Roles)
    private readonly rolesRepo: Repository<Roles>,
    @InjectRepository(SeekerResume)
    private readonly seekerResumesRepository: Repository<SeekerResume>,
    @InjectRepository(UserSessionEntity)
    private readonly sessionRepo: Repository<UserSessionEntity>,
    private readonly areaService: AreaService,
    private readonly educationLevelsService: EducationLevelsService,
  ) { }

  private async getUserFromSessionToken(sessionToken?: string) {
    if (!sessionToken || typeof sessionToken !== 'string') {
      throw new HttpException(
        'Session token is required',
        HttpStatus.UNAUTHORIZED,
      );
    }

    const session = await this.sessionRepo.findOne({
      where: { sessionToken, isActive: 1 },
      relations: ['user'],
    });

    if (!session || !session.user) {
      throw new HttpException('Invalid session', HttpStatus.UNAUTHORIZED);
    }

    if (session.expiresAt && session.expiresAt <= new Date()) {
      throw new HttpException('Session expired', HttpStatus.UNAUTHORIZED);
    }

    return session.user;
  }

  private assertSeekerUser(user: UserEntity) {
    if (user.role !== 'seeker') {
      throw new HttpException(
        'Mobile onboarding is available only for seeker users',
        HttpStatus.FORBIDDEN,
      );
    }
  }

  private getTotalSteps(category?: UserEntity['selectedRoleCategory']) {
    return 2;
  }

  private buildCompletedState(message = 'Onboarding completed') {
    return {
      success: true,
      message,
      onboarding_status: 'completed',
      mobile_onboarding_step: null,
      nextStep: null,
      total_steps: 0,
    };
  }

  private buildReadyToCompleteState(
    category: UserEntity['selectedRoleCategory'],
  ) {
    const totalSteps = this.getTotalSteps(category);

    return {
      success: true,
      selected_role_category: category,
      flow_type: category,
      mobile_onboarding_step: null,
      nextStep: null,
      total_steps: totalSteps,
      onboarding_status: 'in_progress',
      ready_to_complete: true,
      message: 'All onboarding steps completed. Finalize onboarding.',
    };
  }

  private assertOnboardingNotCompleted(user: UserEntity) {
    if (user.onboardingStatus === 'completed') {
      throw new HttpException(
        this.buildCompletedState('Onboarding already completed'),
        HttpStatus.CONFLICT,
      );
    }
  }

  private getValue(...values: any[]) {
    return values.find(
      (value) => value !== undefined && value !== null && value !== '',
    );
  }

  private requireFields(fields: Record<string, any>, names: string[]) {
    const missing = names.filter(
      (name) => this.getValue(fields[name]) === undefined,
    );
    if (missing.length > 0) {
      throw new HttpException(
        `Missing required field(s): ${missing.join(', ')}`,
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  private parseArrayField(value: any, fieldName: string) {
    if (value === undefined) return undefined;
    if (Array.isArray(value)) return value;

    if (typeof value === 'string') {
      try {
        const parsed = JSON.parse(value);
        if (Array.isArray(parsed)) return parsed;
      } catch (error) {
        // Fall through to the consistent error below.
      }
    }

    throw new HttpException(
      `${fieldName} must be a JSON array`,
      HttpStatus.BAD_REQUEST,
    );
  }

  private async parseBlueCollarRoleSelections(fields: Record<string, any>) {
    const rawSelections = this.getValue(
      fields.roleExperiences,
      fields.role_experiences,
      fields.roleIds,
      fields.role_ids,
      fields.roles,
    );

    if (rawSelections === undefined) {
      throw new HttpException(
        'roleIds or roleExperiences is required',
        HttpStatus.BAD_REQUEST,
      );
    }

    const parsedSelections = this.parseArrayField(
      rawSelections,
      'roleExperiences or roleIds',
    );

    const roleExperiences = parsedSelections
      .map((item: any) => {
        const roleId =
          typeof item === 'object'
            ? this.getValue(item.roleId, item.role_id, item.designation_id)
            : item;
        const experienceId =
          typeof item === 'object'
            ? this.getValue(item.experienceId, item.experience_id)
            : undefined;

        return {
          roleId: Number(roleId),
          experienceId: Number(experienceId),
        };
      })
      .filter((item) => !Number.isNaN(item.roleId) && item.roleId > 0);

    const uniqueRoleIds = Array.from(
      new Set(roleExperiences.map((item) => item.roleId)),
    );

    if (uniqueRoleIds.length === 0) {
      throw new HttpException(
        'At least one blue-collar role is required',
        HttpStatus.BAD_REQUEST,
      );
    }

    if (uniqueRoleIds.length > 4) {
      throw new HttpException(
        'Maximum 4 roles can be selected',
        HttpStatus.BAD_REQUEST,
      );
    }

    const missingExperienceRoleIds = roleExperiences
      .filter(
        (item) => !Number.isFinite(item.experienceId) || item.experienceId <= 0,
      )
      .map((item) => item.roleId);

    if (missingExperienceRoleIds.length > 0) {
      throw new HttpException(
        `experienceId is required for role ID(s): ${missingExperienceRoleIds.join(', ')}`,
        HttpStatus.BAD_REQUEST,
      );
    }

    const roles = await this.rolesRepo.find({
      where: { id: In(uniqueRoleIds), category: 'blue_collar', status: 1 },
    });
    const foundRoleIds = new Set(roles.map((role) => role.id));
    const invalidRoleIds = uniqueRoleIds.filter((id) => !foundRoleIds.has(id));

    if (invalidRoleIds.length > 0) {
      throw new HttpException(
        `Invalid blue-collar role ID(s): ${invalidRoleIds.join(', ')}`,
        HttpStatus.BAD_REQUEST,
      );
    }

    return {
      primaryRoleId: uniqueRoleIds[0],
      roleExperiences: roleExperiences.filter(
        (item, index, self) =>
          self.findIndex((existing) => existing.roleId === item.roleId) ===
          index,
      ),
    };
  }

  private buildCommunicationAddressUpdates(fields: Record<string, any>) {
    return {
      communicationAddress: this.getValue(
        fields.communicationAddress,
        fields.communication_address,
        fields.currentAddress,
        fields.current_address,
        fields.address,
      ),
      communicationPincode: this.getValue(
        fields.communicationPincode,
        fields.communication_pincode,
        fields.currentPincode,
        fields.current_pincode,
        fields.pincode,
      ),
      communicationCountryId: this.getValue(
        fields.communicationCountryId,
        fields.communication_country_id,
        fields.currentCountryId,
        fields.current_country_id,
        fields.countryId,
        fields.country_id,
      ),
      communicationStateId: this.getValue(
        fields.communicationStateId,
        fields.communication_state_id,
        fields.currentStateId,
        fields.current_state_id,
        fields.stateId,
        fields.state_id,
      ),
      communicationCityId: this.getValue(
        fields.communicationCityId,
        fields.communication_city_id,
        fields.currentCityId,
        fields.current_city_id,
        fields.cityId,
        fields.city_id,
      ),
      communicationAreaId: this.getValue(
        fields.communicationAreaId,
        fields.communication_area_id,
        fields.currentAreaId,
        fields.current_area_id,
        fields.areaId,
        fields.area_id,
      ),
      sameAsPermanent: false,
    };
  }

  private buildPreferredWorkLocationUpdates(fields: Record<string, any>) {
    return {
      preferredWorkCityId: this.getValue(
        fields.preferredWorkCityId,
        fields.preferred_work_city_id,
        fields.communicationCityId,
        fields.communication_city_id,
        fields.currentCityId,
        fields.current_city_id,
        fields.cityId,
        fields.city_id,
      ),
      preferredWorkLocalityId: this.getValue(
        fields.preferredWorkLocalityId,
        fields.preferred_work_locality_area_id,
        fields.preferred_work_locality_id,
        fields.communicationAreaId,
        fields.communication_area_id,
        fields.currentAreaId,
        fields.current_area_id,
        fields.areaId,
        fields.area_id,
      ),
    };
  }

  private async enrichAddressFromPincode(addressUpdates: Record<string, any>) {
    const pincode = addressUpdates.communicationPincode;

    if (!pincode) {
      return addressUpdates;
    }

    const areas = await this.areaService.getAreasByPincode(String(pincode), 1);
    if (!Array.isArray(areas) || areas.length === 0) {
      return addressUpdates;
    }

    const requestedAreaId = Number(addressUpdates.communicationAreaId);
    const matchedArea =
      areas.find((area) => Number(area.id) === requestedAreaId) || areas[0];

    return {
      ...addressUpdates,
      communicationCountryId:
        addressUpdates.communicationCountryId || matchedArea.country_id,
      communicationStateId:
        addressUpdates.communicationStateId || matchedArea.state_id,
      communicationCityId:
        addressUpdates.communicationCityId || matchedArea.city_id,
      communicationAreaId: addressUpdates.communicationAreaId || matchedArea.id,
      pincode_mapping: {
        pincode: matchedArea.pincode,
        area_id: matchedArea.id,
        area: matchedArea.area,
        city_id: matchedArea.city_id,
        city_name: matchedArea.city_name,
        state_id: matchedArea.state_id,
        state_name: matchedArea.state_name,
        country_id: matchedArea.country_id,
        country_name: matchedArea.country_name,
      },
    };
  }

  private validateStepForCategory(
    stepNum: number,
    category: UserEntity['selectedRoleCategory'],
  ) {
    if (!Number.isInteger(stepNum) || stepNum < 1) {
      throw new HttpException('Invalid step', HttpStatus.BAD_REQUEST);
    }

    if (stepNum > 1 && !category) {
      throw new HttpException(
        'Role category must be selected before continuing onboarding',
        HttpStatus.BAD_REQUEST,
      );
    }

    const totalSteps = this.getTotalSteps(category);
    if (stepNum > totalSteps) {
      throw new HttpException(
        `Step ${stepNum} is not valid for ${category || 'unselected'} onboarding`,
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  private serializeHighestQualifications(educationLevels: any[]) {
    return (educationLevels || []).map((level) => ({
      id: level.id,
      name: level.name,
    }));
  }

  private async getHighestQualificationOptions() {
    const educationLevels = await this.educationLevelsService.findAll();
    return this.serializeHighestQualifications(educationLevels);
  }

  @Public()
  @Get('options/highest_qualifications')
  @ApiOperation({
    summary: 'Get highest qualification options for mobile onboarding',
  })
  async getHighestQualifications() {
    return {
      success: true,
      highestQualifications: await this.getHighestQualificationOptions(),
    };
  }

  @Public()
  @Get()
  @ApiOperation({ summary: 'Get current onboarding state' })
  @ApiQuery({
    name: 'sessionToken',
    required: true,
    description: 'Unique session token from login',
  })
  async getOnboardingState(@Req() req: any, @Query() query: any) {
    const sessionToken =
      req?.headers?.authorization?.replace('Bearer ', '')?.trim() ||
      query?.sessionToken ||
      query?.session_token;
    const user = await this.getUserFromSessionToken(sessionToken);
    this.assertSeekerUser(user);

    if (user.onboardingStatus === 'completed') {
      return this.buildCompletedState();
    }

    const data: any = await this.userService.getOnboardingData(user.id);
    const highestQualifications = await this.getHighestQualificationOptions();

    const category = user.selectedRoleCategory;
    const totalSteps = this.getTotalSteps(category);

    let currentStep = user.mobileOnboardingStep;
    const readyToComplete =
      currentStep === 0 ||
      (currentStep === null &&
        this.mobileOnboardingStateService.isReadyToComplete(user, data));

    if (readyToComplete) {
      if (currentStep === null) {
        await this.userService.update(user.id, { mobileOnboardingStep: 0 });
      }

      return this.buildReadyToCompleteState(category);
    }

    if (user.onboardingStatus === 'in_progress' && currentStep === null) {
      currentStep = 1;
    }

    return {
      selected_role_category: category,
      flow_type: category, // Alias for easier mobile consumption
      mobile_onboarding_step: currentStep,
      total_steps: totalSteps,
      onboarding_status: user.onboardingStatus,
      ready_to_complete: false,
      options: {
        highestQualifications,
      },
      formData: {
        // Flattened Profile & Address Fields (mapped to camelCase for Mobile App)
        fullName: user.fullName === 'Guest User' ? '' : user.fullName,
        email: user.email,
        phone: user.phone,
        role: user.role,
        profilePicture: user.profilePicture,
        gender: data?.profile?.gender,
        dateOfBirth: data?.profile?.date_of_birth,
        maritalStatus: data?.profile?.marital_status,
        professionId: data?.profile?.profession_id,
        professionalTitle: data?.profile?.professional_title,
        biography: data?.profile?.bio_graphy,
        educationId: data?.profile?.education_id,
        experiencesId: data?.profile?.experiences_id,
        availability: data?.profile?.your_availability,
        personalWebsite: data?.profile?.personal_website,
        skills: data?.profile?.skills_ids || [],
        languages: data?.profile?.languages_ids || [],
        preferredWorkCityId: data?.profile?.preferred_work_city_id,
        preferredWorkLocalityId: data?.profile?.preferred_work_locality_area_id,

        // Flattened Address fields
        permanentAddress: data?.address?.permanent_address,
        permanentPincode: data?.address?.permanent_pincode,
        permanentCityId: data?.address?.permanent_city_id,
        permanentStateId: data?.address?.permanent_state_id,
        permanentCountryId: data?.address?.permanent_country_id,
        permanentAreaId: data?.address?.permanent_area_id,
        communicationAddress: data?.address?.communication_address,
        communicationPincode: data?.address?.communication_pincode,
        communicationCityId: data?.address?.communication_city_id,
        communicationStateId: data?.address?.communication_state_id,
        communicationCountryId: data?.address?.communication_country_id,
        communicationAreaId: data?.address?.communication_area_id,
        sameAsPermanent:
          data?.address?.same_as_permanent === 1 ||
          data?.address?.same_as_permanent === true,

        // Lists (Mapped to camelCase for Mobile App)
        educations: (data?.educations || []).map((edu: any) => ({
          education_id: edu.education_id,
          degree: edu.degree,
          year: edu.year,
          notes: edu.notes,
        })),
        experiences: (data?.experiences || []).map((exp: any) => ({
          company: exp.company,
          department: exp.department,
          designation: exp.designation,
          startDate: exp.start_date,
          endDate: exp.end_date,
          current: exp.current,
          responsibilities: exp.responsibilities,
        })),
        resumes: data?.resumes || [],
        roleExperiences: (data?.roleExperiences || []).map((re: any) => ({
          roleId: re.designation_id,
          experienceId: re.experience_id,
        })),
      },
    };
  }

  @Public()
  @Put()
  @ApiOperation({ summary: 'Update onboarding data by step' })
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(
    FileFieldsInterceptor(
      [
        { name: 'profilePicture', maxCount: 1 },
        { name: 'resume', maxCount: 1 },
      ],
      {
        storage: diskStorage({
          destination: (req, file, cb) => {
            const folder =
              file.fieldname === 'profilePicture'
                ? './uploads/profile-pictures'
                : './uploads/resumes';
            cb(null, folder);
          },
          filename: (req, file, cb) => {
            const uniqueSuffix =
              Date.now() + '-' + Math.round(Math.random() * 1e9);
            cb(null, uniqueSuffix + extname(file.originalname));
          },
        }),
        limits: { fileSize: 5 * 1024 * 1024 },
        fileFilter: (req, file, cb) => {
          if (
            file.fieldname === 'resume' &&
            !file.originalname.match(/\.(pdf|doc|docx)$/i)
          ) {
            return cb(
              new Error('Only PDF, DOC, and DOCX resumes are allowed!'),
              false,
            );
          }
          cb(null, true);
        },
      },
    ),
  )
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        sessionToken: { type: 'string', description: 'Unique session token' },
        step: { type: 'number', description: 'Current onboarding step (1-2)' },
        // Onboarding Data Fields
        professionId: {
          type: 'number',
          description:
            'Preferred role/profession ID. For white-collar, maps to seeker_profiles.profession_id. For blue-collar step 1, this selects the category and initial preferred role.',
        },
        fullName: { type: 'string' },
        email: { type: 'string' },
        gender: { type: 'string', enum: ['Male', 'Female', 'Other'] },
        dateOfBirth: {
          type: 'string',
          example: '1990-05-15',
          description: 'Date of birth in YYYY-MM-DD format.',
        },
        educationId: {
          type: 'number',
          description:
            'White-collar profile-level education ID stored in seeker_profiles.education_id.',
        },
        highestQualificationId: {
          type: 'number',
          description:
            'White-collar step 2 highest qualification ID. Alias for educationId.',
        },
        experiencesId: {
          type: 'number',
          description:
            'White-collar profile-level experience level ID stored in seeker_profiles.experiences_id. This is not the work-history list.',
        },
        experienceId: {
          type: 'number',
          description:
            'White-collar step 2 experience level ID. Alias for experiencesId.',
        },
        professionalTitle: {
          type: 'string',
          description: 'White-collar professional title.',
        },
        jobTitle: {
          type: 'string',
          description:
            'White-collar step 2 job title. Alias for professionalTitle.',
        },
        biography: {
          type: 'string',
          description: 'White-collar profile biography.',
        },
        availability: {
          type: 'string',
          description:
            'White-collar availability value stored in seeker_profiles.your_availability.',
        },
        personalWebsite: {
          type: 'string',
          description: 'White-collar personal website URL.',
        },
        skills: {
          type: 'string',
          description:
            'Required JSON array of white-collar skill IDs, for example [1,2,5].',
        },
        skillIds: {
          type: 'string',
          description: 'Required alias for skills, for example [1,2,5].',
        },
        educations: {
          type: 'string',
          description:
            'JSON array of white-collar education rows. Distinct from educationId, which is the profile-level education value.',
          example:
            '[{\"education_id\":4,\"degree\":\"MBA\",\"year\":\"2015\",\"notes\":\"\"}]',
        },
        experiences: {
          type: 'string',
          description:
            'JSON array of white-collar work-history rows. Distinct from experiencesId, which is the profile-level experience level.',
          example:
            '[{\"company\":\"Google\",\"department\":\"Engineering\",\"designation\":\"Engineer\",\"startDate\":\"2020-01-01\",\"endDate\":null,\"current\":true,\"responsibilities\":\"Built internal systems\"}]',
        },
        preferredWorkCityId: {
          type: 'number',
          description: 'Blue-collar preferred work city ID.',
        },
        preferredWorkLocalityId: {
          type: 'number',
          description: 'Blue-collar preferred work locality/area ID.',
        },
        roleExperiences: {
          type: 'string',
          description:
            'JSON array of blue-collar role selections. Each item can contain roleId and optional experienceId. Maximum 4 roles.',
          example:
            '[{\"roleId\":43,\"experienceId\":2},{\"roleId\":44,\"experienceId\":1}]',
        },
        roleIds: {
          type: 'string',
          description:
            'Alternative blue-collar step 2 payload: JSON array of role IDs, for example [43,44]. Maximum 4 roles.',
          example: '[43,44]',
        },
        permanentAddress: { type: 'string' },
        permanentPincode: { type: 'string' },
        permanentCityId: { type: 'number' },
        permanentStateId: { type: 'number' },
        permanentCountryId: { type: 'number' },
        permanentAreaId: { type: 'number' },
        communicationAddress: { type: 'string' },
        communicationPincode: { type: 'string' },
        communicationCityId: { type: 'number' },
        communicationStateId: { type: 'number' },
        communicationCountryId: { type: 'number' },
        communicationAreaId: { type: 'number' },
        sameAsPermanent: {
          type: 'boolean',
          description:
            'Whether communication address is same as permanent address.',
        },
        profilePicture: { type: 'string', format: 'binary' },
        resume: { type: 'string', format: 'binary' },
        resumeName: {
          type: 'string',
          description: 'Optional resume display name for upload step.',
        },
      },
      required: ['sessionToken', 'step'],
    },
  })
  async update(
    @Req() req: any,
    @Body() body: any,
    @UploadedFiles() files: any,
  ) {
    const { step, ...fields } = body;
    const sessionToken =
      req?.headers?.authorization?.replace('Bearer ', '')?.trim() ||
      body?.sessionToken ||
      body?.session_token;
    const user = await this.getUserFromSessionToken(sessionToken);
    this.assertSeekerUser(user);
    this.assertOnboardingNotCompleted(user);

    if (!step) {
      throw new HttpException('Step is required', HttpStatus.BAD_REQUEST);
    }

    const stepNum = Number(step);
    this.validateStepForCategory(stepNum, user.selectedRoleCategory);

    // Role to Category Mapping
    const mapRoleToCategory = async (role_id: number) => {
      const roleEntity = await this.rolesRepo.findOne({
        where: { id: role_id },
      });
      if (!roleEntity) return 'white_collar';
      return roleEntity.category || 'white_collar';
    };

    const currentCategory = user.selectedRoleCategory;
    let nextStepValue = stepNum + 1;
    let pincodeMapping: any = null;
    let uploadedResume: any = null;

    switch (stepNum) {
      case 1: {
        // Only allow specific profile fields in Step 1 to prevent accidental overwrites
        const profileUpdates: any = {};
        const highestQualificationId = this.getValue(
          fields.highestQualificationId,
          fields.highest_qualification_id,
          fields.educationId,
          fields.education_id,
        );
        const allowedFields = [
          'fullName',
          'email',
          'phone',
          'gender',
          'maritalStatus',
          'professionId',
          'dateOfBirth',
          'educationId',
        ];
        allowedFields.forEach((f) => {
          if (fields[f] !== undefined) profileUpdates[f] = fields[f];
        });
        if (highestQualificationId !== undefined) {
          profileUpdates.educationId = highestQualificationId;
        }

        // 1. Handle Role Update (Instant)
        let category = user.selectedRoleCategory;

        if (fields.professionId || fields.profession_id) {
          const professionId = parseInt(
            fields.professionId || fields.profession_id,
          );
          const roleEntity = await this.rolesRepo.findOne({
            where: { id: professionId },
          });
          if (!roleEntity) {
            throw new HttpException('Invalid role ID', HttpStatus.BAD_REQUEST);
          }

          category = await mapRoleToCategory(professionId);
          await this.userService.update(user.id, {
            selectedRoleCategory: category,
          });

          profileUpdates.profession_id = professionId;
          profileUpdates.selected_role_category = category;

          // AUTOMAPPING 1: White Collar / Blue Collar - Set professional title to role name
          if (category === 'white_collar' || category === 'blue_collar') {
            profileUpdates.professional_title = roleEntity.name;
          }

          // Return immediately if it's just a role update (minimal fields)
          if (
            Object.keys(fields).length <= 4 &&
            !fields.fullName &&
            !fields.gender
          ) {
            await this.userService.updateOnboardingData(
              user.id,
              profileUpdates,
            );
            await this.userService.update(user.id, { mobileOnboardingStep: 1 });
            return {
              success: true,
              category,
              message: 'Role updated instantly',
              nextStep: 1,
              totalSteps: 2,
            };
          }
        }

        const baseStepOneFields = {
          professionId:
            fields.professionId ||
            fields.profession_id ||
            profileUpdates.profession_id,
          fullName: fields.fullName,
          phone: fields.phone,
          email: fields.email,
          dateOfBirth: fields.dateOfBirth || fields.date_of_birth,
          gender: fields.gender,
        };

        if (category === 'blue_collar') {
          const preferredWorkLocation =
            this.buildPreferredWorkLocationUpdates(fields);

          this.requireFields(
            {
              ...baseStepOneFields,
              preferredWorkCityId: preferredWorkLocation.preferredWorkCityId,
              preferredWorkLocalityId:
                preferredWorkLocation.preferredWorkLocalityId,
            },
            [
              'professionId',
              'fullName',
              'phone',
              'dateOfBirth',
              'gender',
              'preferredWorkCityId',
              'preferredWorkLocalityId',
            ],
          );

          Object.assign(profileUpdates, preferredWorkLocation);
        } else {
          const addressUpdates = await this.enrichAddressFromPincode(
            this.buildCommunicationAddressUpdates(fields),
          );
          const requiredStepOneFields = [
            'professionId',
            'fullName',
            'phone',
            'email',
            'dateOfBirth',
            'gender',
            'communicationAddress',
            'communicationPincode',
            'communicationCityId',
          ];

          this.requireFields(
            {
              ...baseStepOneFields,
              communicationAddress: addressUpdates.communicationAddress,
              communicationPincode: addressUpdates.communicationPincode,
              communicationCityId: addressUpdates.communicationCityId,
            },
            requiredStepOneFields,
          );

          const { pincode_mapping, ...addressPayload } = addressUpdates;
          pincodeMapping = pincode_mapping || null;
          Object.assign(profileUpdates, addressPayload);
        }

        profileUpdates.mobileOnboardingStep = stepNum + 1;
        nextStepValue = stepNum + 1;

        await this.userService.updateOnboardingData(user.id, profileUpdates);
        break;
      }

      case 2: {
        if (currentCategory === 'blue_collar') {
          nextStepValue = 0;
          // Blue Collar Step 2: select up to 4 trade roles, e.g. plumber.
          const { primaryRoleId, roleExperiences } =
            await this.parseBlueCollarRoleSelections(fields);
          const profileData: any = {};
          profileData.professionId = primaryRoleId;
          profileData.roleExperiences = roleExperiences;
          profileData.mobileOnboardingStep = nextStepValue;
          await this.userService.updateOnboardingData(user.id, profileData);

          if (files?.resume?.[0]) {
            const resumePath = files.resume[0].path.replace(/\\/g, '/');
            const seekerData = await this.userService.getOnboardingData(
              user.id,
            );
            if (seekerData?.profile) {
              const savedResume = await this.seekerResumesRepository.save({
                seeker_id: seekerData.profile.id,
                file_url: resumePath,
                name:
                  fields.resumeName || files.resume[0].originalname || 'Resume',
                uploaded_at: new Date(),
              });
              uploadedResume = {
                id: savedResume.id,
                name: savedResume.name,
                file_url: savedResume.file_url,
                uploaded_at: savedResume.uploaded_at,
              };
            }
          }
        } else {
          nextStepValue = 0;
          // White Collar Step 2: professional details.
          this.requireFields(
            {
              educationId: this.getValue(
                fields.educationId,
                fields.education_id,
                fields.highestQualificationId,
                fields.highest_qualification_id,
              ),
              professionalTitle: this.getValue(
                fields.professionalTitle,
                fields.professional_title,
                fields.jobTitle,
                fields.job_title,
              ),
              experiencesId: this.getValue(
                fields.experiencesId,
                fields.experienceId,
                fields.experiences_id,
                fields.experience_id,
              ),
              skills: this.getValue(
                fields.skills,
                fields.skillIds,
                fields.skill_ids,
                fields.skillsIds,
                fields.skills_ids,
              ),
            },
            ['educationId', 'professionalTitle', 'experiencesId', 'skills'],
          );

          const stepUpdates: any = {
            educationId: this.getValue(
              fields.educationId,
              fields.education_id,
              fields.highestQualificationId,
              fields.highest_qualification_id,
            ),
            professionalTitle: this.getValue(
              fields.professionalTitle,
              fields.professional_title,
              fields.jobTitle,
              fields.job_title,
            ),
            experiencesId: this.getValue(
              fields.experiencesId,
              fields.experienceId,
              fields.experiences_id,
              fields.experience_id,
            ),
            mobileOnboardingStep: nextStepValue,
          };

          const skillsValue = this.getValue(
            fields.skills,
            fields.skillIds,
            fields.skill_ids,
            fields.skillsIds,
            fields.skills_ids,
          );
          const parsedSkills = this.parseArrayField(skillsValue, 'skills');
          if (!parsedSkills || parsedSkills.length === 0) {
            throw new HttpException(
              'At least one skill is required',
              HttpStatus.BAD_REQUEST,
            );
          }
          stepUpdates.skills = parsedSkills;

          const optionalProfileFields = [
            'availability',
            'biography',
            'bio',
            'personalWebsite',
          ];
          optionalProfileFields.forEach((field) => {
            if (fields[field] !== undefined) stepUpdates[field] = fields[field];
          });

          await this.userService.updateOnboardingData(user.id, stepUpdates);

          if (files?.resume?.[0]) {
            const resumePath = files.resume[0].path.replace(/\\/g, '/');
            const seekerData = await this.userService.getOnboardingData(
              user.id,
            );
            if (seekerData?.profile) {
              const savedResume = await this.seekerResumesRepository.save({
                seeker_id: seekerData.profile.id,
                file_url: resumePath,
                name:
                  fields.resumeName || files.resume[0].originalname || 'Resume',
                uploaded_at: new Date(),
              });
              uploadedResume = {
                id: savedResume.id,
                name: savedResume.name,
                file_url: savedResume.file_url,
                uploaded_at: savedResume.uploaded_at,
              };
            }
          }
        }
        break;
      }

      // Step 3: Profile Details — biography, skills, languages, availability, personalWebsite
      case 3: {
        if (currentCategory === 'blue_collar') {
          throw new HttpException(
            'Step 3 is not valid for blue_collar onboarding',
            HttpStatus.BAD_REQUEST,
          );
        } else {
          const step3Updates: any = {};

          const step3ProfileFields = [
            'professionId',
            'experiencesId',
            'availability',
            'biography',
            'personalWebsite',
            'professionalTitle',
          ];
          step3ProfileFields.forEach((f) => {
            if (fields[f] !== undefined) step3Updates[f] = fields[f];
          });

          // EDGE CASE: If role is refined in Step 3, re-evaluate category
          if (fields.professionId || fields.profession_id) {
            const pId = parseInt(fields.professionId || fields.profession_id);
            const cat = await mapRoleToCategory(pId);
            await this.userService.update(user.id, {
              selectedRoleCategory: cat,
            });
            step3Updates.profession_id = pId;
            step3Updates.selected_role_category = cat;
          }

          if (fields.skills !== undefined) {
            step3Updates.skills =
              typeof fields.skills === 'string'
                ? JSON.parse(fields.skills)
                : fields.skills;
          }
          if (fields.languages !== undefined) {
            step3Updates.languages =
              typeof fields.languages === 'string'
                ? JSON.parse(fields.languages)
                : fields.languages;
          }

          step3Updates.mobileOnboardingStep = nextStepValue;
          await this.userService.updateOnboardingData(user.id, step3Updates);
        }
        break;
      }

      // Step 4: Education history
      case 4: {
        const step4Updates: any = {};

        if (fields.educations !== undefined) {
          step4Updates.educations =
            typeof fields.educations === 'string'
              ? JSON.parse(fields.educations)
              : fields.educations;
        }
        if (fields.educationId !== undefined) {
          step4Updates.educationId = fields.educationId;
        }

        step4Updates.mobileOnboardingStep = nextStepValue;
        await this.userService.updateOnboardingData(user.id, step4Updates);
        break;
      }

      // Step 5: Work experience history
      case 5: {
        const step5Updates: any = {};

        if (fields.experiences !== undefined) {
          step5Updates.experiences =
            typeof fields.experiences === 'string'
              ? JSON.parse(fields.experiences)
              : fields.experiences;
        }

        step5Updates.mobileOnboardingStep = nextStepValue;
        await this.userService.updateOnboardingData(user.id, step5Updates);
        break;
      }

      case 6: {
        // Step 6: Assets Upload
        const assetUpdates: any = {};
        if (files?.profilePicture?.[0]) {
          assetUpdates.profilePicture = files.profilePicture[0].path.replace(
            /\\/g,
            '/',
          );
        }

        if (files?.resume?.[0]) {
          const resumePath = files.resume[0].path.replace(/\\/g, '/');
          const seekerData = await this.userService.getOnboardingData(user.id);
          if (seekerData?.profile) {
            await this.seekerResumesRepository.save({
              seeker_id: seekerData.profile.id,
              file_url: resumePath,
              name:
                fields.resumeName || files.resume[0].originalname || 'Resume',
              uploaded_at: new Date(),
            });
          }
        }

        if (Object.keys(assetUpdates).length > 0) {
          await this.userService.update(user.id, assetUpdates);
        }

        nextStepValue = 0;
        await this.userService.update(user.id, {
          mobileOnboardingStep: nextStepValue,
        });
        break;
      }

      default: {
        throw new HttpException('Invalid step', HttpStatus.BAD_REQUEST);
      }
    }

    const finalCategory =
      currentCategory ||
      (stepNum === 1 && fields.professionId
        ? await mapRoleToCategory(parseInt(fields.professionId))
        : null);
    const totalSteps = 2;

    return {
      success: true,
      message: `Step ${stepNum} saved successfully`,
      nextStep: nextStepValue === 0 ? null : nextStepValue,
      totalSteps,
      category: finalCategory,
      ready_to_complete: nextStepValue === 0,
      pincode_mapping: pincodeMapping,
      uploaded_resume: uploadedResume,
    };
  }

  @Public()
  @Post('complete')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        sessionToken: { type: 'string' },
      },
      required: ['sessionToken'],
    },
  })
  async complete(@Req() req: any, @Body() body: { sessionToken?: string }) {
    const sessionToken =
      req?.headers?.authorization?.replace('Bearer ', '')?.trim() ||
      body?.sessionToken;
    const user = await this.getUserFromSessionToken(sessionToken);
    this.assertSeekerUser(user);

    if (user.onboardingStatus === 'completed') {
      return this.buildCompletedState('Onboarding already completed');
    }

    const category = user.selectedRoleCategory;
    const profileData: any = await this.userService.getOnboardingData(user.id);

    if (category !== 'white_collar' && category !== 'blue_collar') {
      throw new HttpException(
        'Role category is required before completing onboarding',
        HttpStatus.BAD_REQUEST,
      );
    }

    if (user.mobileOnboardingStep !== null && user.mobileOnboardingStep !== 0) {
      throw new HttpException(
        'Complete all onboarding steps before finalizing onboarding',
        HttpStatus.BAD_REQUEST,
      );
    }

    // Validation Logic
    if (category === 'white_collar') {
      if (!user.email) {
        throw new HttpException('Email is required', HttpStatus.BAD_REQUEST);
      }
      if (!profileData?.profile?.profession_id) {
        throw new HttpException(
          'Profession is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!user.fullName || user.fullName === 'Guest User') {
        throw new HttpException(
          'Full name is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!user.phone) {
        throw new HttpException(
          'Phone number is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.date_of_birth) {
        throw new HttpException(
          'Date of birth is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.gender) {
        throw new HttpException('Gender is required', HttpStatus.BAD_REQUEST);
      }
      if (!profileData?.address?.communication_address) {
        throw new HttpException(
          'Communication address is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.education_id) {
        throw new HttpException(
          'Highest qualification is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.experiences_id) {
        throw new HttpException(
          'Experience is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.professional_title) {
        throw new HttpException(
          'Professional title is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      const skills = profileData?.profile?.skills_ids;
      if (!Array.isArray(skills) || skills.length === 0) {
        throw new HttpException('Skills are required', HttpStatus.BAD_REQUEST);
      }
    } else if (category === 'blue_collar') {
      if (!profileData?.profile?.profession_id) {
        throw new HttpException(
          'Preferred role is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!user.fullName || user.fullName === 'Guest User') {
        throw new HttpException(
          'Full name is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!user.phone) {
        throw new HttpException(
          'Phone number is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.date_of_birth) {
        throw new HttpException(
          'Date of birth is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.gender) {
        throw new HttpException('Gender is required', HttpStatus.BAD_REQUEST);
      }
      if (!profileData?.profile?.preferred_work_city_id) {
        throw new HttpException(
          'Preferred work city is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (!profileData?.profile?.preferred_work_locality_area_id) {
        throw new HttpException(
          'Preferred work locality is required',
          HttpStatus.BAD_REQUEST,
        );
      }
      if (
        !profileData?.roleExperiences ||
        profileData.roleExperiences.length === 0
      ) {
        throw new HttpException(
          'Role experiences are required',
          HttpStatus.BAD_REQUEST,
        );
      }
    }

    await this.userService.update(user.id, {
      onboardingStatus: 'completed',
      onboardingStep: null,
      mobileOnboardingStep: null,
    });

    // Seeker registration success notification trigger at complete of onboarding
    if (user.role === 'seeker') {
      try {
        const templates = await this.rolesRepo.manager.query(
          `SELECT id FROM whatsapp_templates WHERE name = 'seeker_registration_success' AND is_active = 1 LIMIT 1`,
        );
        if (templates && templates.length > 0) {
          const templateId = templates[0].id;
          const candidateName = user.fullName || 'Seeker';
          await this.rolesRepo.manager.query(
            `INSERT INTO notification_events (template_id, recipient_user_id, reference_type, reference_id, payload, status) 
             VALUES (?, ?, ?, ?, ?, ?)`,
            [
              templateId,
              user.id,
              null,
              null,
              JSON.stringify({ candidate_name: candidateName }),
              'pending',
            ],
          );
          console.log(
            `[ONBOARDING] Seeker registration notification event created via mobile onboarding complete for userId: ${user.id}`,
          );
        }
      } catch (err) {
        console.error(
          'Failed to create registration notification event during mobile onboarding completion:',
          err,
        );
      }
    }

    return {
      success: true,
      message: 'Onboarding completed successfully',
    };
  }
}
