import {
  Controller,
  Get,
  Put,
  Post,
  Body,
  Query,
  HttpException,
  HttpStatus,
  UseInterceptors,
  UploadedFiles,
} 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 { Repository, DataSource } 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';

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

  @Public()
  @Get()
  @ApiOperation({ summary: 'Get current onboarding state' })
  @ApiQuery({
    name: 'sessionToken',
    required: true,
    description: 'Unique session token from login',
  })
  async getOnboardingState(@Query() query: any) {
    const { phone, role, sessionToken } = query;
    let user;

    if (sessionToken) {
      const session = await this.sessionRepo.findOne({
        where: { sessionToken, isActive: 1 },
        relations: ['user'],
      });
      if (session) user = session.user;
    } else if (phone) {
      if (role) {
        user = await this.userService.findByPhoneAndRole(phone, role);
      } else {
        const users = await this.dataSource
          .getRepository(UserEntity)
          .find({ where: { phone } });
        user =
          users.find((u) => u.onboardingStatus === 'in_progress') || users[0];
      }
    }

    if (!user) {
      throw new HttpException(
        'User identity not provided or invalid session',
        HttpStatus.UNAUTHORIZED,
      );
    }

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

    const category = user.selectedRoleCategory;
    const totalSteps = category === 'blue_collar' ? 2 : 6;

    let currentStep = user.mobileOnboardingStep;
    if (user.onboardingStatus === 'in_progress' && currentStep === null) {
      currentStep = totalSteps;
    }

    return {
      selected_role_category: category,
      flow_type: category, // Alias for easier mobile consumption
      mobile_onboarding_step: currentStep,
      total_steps: totalSteps,
      onboarding_status: user.onboardingStatus,
      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));
          },
        }),
      },
    ),
  )
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        sessionToken: { type: 'string', description: 'Unique session token' },
        step: { type: 'number', description: 'Current onboarding step (1-6)' },
        // Onboarding Data Fields
        professionId: {
          type: 'number',
          description:
            'Desired role/profession ID. For white-collar, maps to seeker_profiles.profession_id. For blue-collar, used for role/category selection.',
        },
        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.',
        },
        experiencesId: {
          type: 'number',
          description:
            'White-collar profile-level experience level ID stored in seeker_profiles.experiences_id. This is not the work-history list.',
        },
        professionalTitle: {
          type: 'string',
          description: 'White-collar professional title.',
        },
        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:
            'JSON array of white-collar skill IDs, for example [1,2,5].',
          example: '[1,2,5]',
        },
        languages: {
          type: 'string',
          description: 'JSON array of language IDs, for example [1,2].',
          example: '[1,2]',
        },
        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-wise experience selections. Each item contains roleId and experienceId.',
          example:
            '[{\"roleId\":43,\"experienceId\":2},{\"roleId\":44,\"experienceId\":1}]',
        },
        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(@Body() body: any, @UploadedFiles() files: any) {
    const { phone, role: userRole, step, sessionToken, ...fields } = body;
    let user;

    if (sessionToken) {
      const session = await this.sessionRepo.findOne({
        where: { sessionToken, isActive: 1 },
        relations: ['user'],
      });
      if (session) user = session.user;
    } else if (phone && userRole) {
      user = await this.userService.findByPhoneAndRole(phone, userRole);
    } else if (phone) {
      const users = await this.dataSource
        .getRepository(UserEntity)
        .find({ where: { phone } });
      user =
        users.find((u) => u.onboardingStatus === 'in_progress') || users[0];
    }

    if (!user) {
      throw new HttpException(
        'User identity not provided or invalid session',
        HttpStatus.UNAUTHORIZED,
      );
    }

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

    const stepNum = parseInt(step);
    const updates: any = { ...fields };

    // 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;

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

        // 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: category === 'blue_collar' ? 2 : 6,
            };
          }
        }

        // 4. Determine final step for this request
        if (fields.fullName && fields.gender) {
          profileUpdates.mobileOnboardingStep = stepNum + 1;
          nextStepValue = stepNum + 1;
        } else {
          nextStepValue = stepNum;
        }

        // 3. Save everything through updateOnboardingData (which handles both User and Profile tables)
        await this.userService.updateOnboardingData(user.id, profileUpdates);
        break;
      }

      case 2: {
        if (currentCategory === 'blue_collar') {
          nextStepValue = null; // Ready to complete
          // Blue Collar Step 2: Location + Role Experience
          const profileData: any = {};
          if (fields.preferredWorkCityId)
            profileData.preferredWorkCityId = fields.preferredWorkCityId;
          if (fields.preferredWorkLocalityId)
            profileData.preferredWorkLocalityId =
              fields.preferredWorkLocalityId;

          if (fields.roleExperiences) {
            const roleExperiences =
              typeof fields.roleExperiences === 'string'
                ? JSON.parse(fields.roleExperiences)
                : fields.roleExperiences;

            if (Array.isArray(roleExperiences) && roleExperiences.length > 4) {
              throw new HttpException(
                'Maximum 4 roles can be selected',
                HttpStatus.BAD_REQUEST,
              );
            }
            profileData.roleExperiences = roleExperiences;
          }

          profileData.mobileOnboardingStep = nextStepValue;
          await this.userService.updateOnboardingData(user.id, profileData);
        } else {
          // White Collar Step 2: Address Details
          const stepUpdates: any = {};

          // Address fields
          const allowedAddressFields = [
            'permanentAddress',
            'permanentPincode',
            'permanentCityId',
            'permanentStateId',
            'permanentCountryId',
            'permanentAreaId',
            'communicationAddress',
            'communicationPincode',
            'communicationCityId',
            'communicationStateId',
            'communicationCountryId',
            'communicationAreaId',
            'sameAsPermanent',
          ];
          allowedAddressFields.forEach((f) => {
            if (fields[f] !== undefined) stepUpdates[f] = fields[f];
          });

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

      // Step 3: Profile Details — biography, skills, languages, availability, personalWebsite
      case 3: {
        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 = null;
        await this.userService.update(user.id, {
          mobileOnboardingStep: nextStepValue,
        });
        break;
      }

      default: {
        nextStepValue = stepNum + 1;
        const normalizedUpdates: any = { ...updates };

        if (typeof normalizedUpdates.experiences === 'string') {
          normalizedUpdates.experiences = JSON.parse(
            normalizedUpdates.experiences,
          );
        }
        if (typeof normalizedUpdates.educations === 'string') {
          normalizedUpdates.educations = JSON.parse(
            normalizedUpdates.educations,
          );
        }
        if (typeof normalizedUpdates.skills === 'string') {
          normalizedUpdates.skills = JSON.parse(normalizedUpdates.skills);
        }
        if (typeof normalizedUpdates.languages === 'string') {
          normalizedUpdates.languages = JSON.parse(normalizedUpdates.languages);
        }

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

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

    return {
      success: true,
      message: `Step ${stepNum} saved successfully`,
      nextStep: nextStepValue,
      totalSteps,
      category: finalCategory,
    };
  }

  @Public()
  @Post('complete')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        sessionToken: { type: 'string' },
      },
      required: ['sessionToken'],
    },
  })
  async complete(
    @Body() body: { phone: string; role?: string; sessionToken?: string },
  ) {
    const { phone, role, sessionToken } = body;
    let user;

    if (sessionToken) {
      const session = await this.sessionRepo.findOne({
        where: { sessionToken, isActive: 1 },
        relations: ['user'],
      });
      if (session) user = session.user;
    } else if (phone) {
      if (role) {
        user = await this.userService.findByPhoneAndRole(phone, role);
      } else {
        const users = await this.dataSource
          .getRepository(UserEntity)
          .find({ where: { phone } });
        user =
          users.find((u) => u.onboardingStatus === 'in_progress') || users[0];
      }
    }

    if (!user) {
      throw new HttpException(
        'User identity not provided or invalid session',
        HttpStatus.UNAUTHORIZED,
      );
    }

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

    // Validation Logic
    if (category === 'white_collar') {
      if (!user.email)
        throw new HttpException('Email is required', HttpStatus.BAD_REQUEST);
      // Add other white collar validations here
    } else if (category === 'blue_collar') {
      // Blue collar only needs Role Experience and Location (from Step 2)
      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,
    });

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