import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Job } from './entity/job.entity';
import { CreateJobDto } from './dto/create-job.dto';
import { UpdateJobDto } from './dto/update-job.dto';
import { QueryJobsDto } from './dto/query-jobs.dto';
import slugify from 'slugify';

@Injectable()
export class JobsService {
    constructor(
        @InjectRepository(Job)
        private jobRepository: Repository<Job>,
    ) { }

    async create(createJobDto: CreateJobDto): Promise<Job> {
        const newJob = this.jobRepository.create({
            ...createJobDto,
            slug: slugify(createJobDto.title, { lower: true }),
            postedAt: createJobDto.postedAt ? new Date(createJobDto.postedAt) : new Date(),
        });

        return this.jobRepository.save(newJob);
    }

    async findAll(query: QueryJobsDto): Promise<Job[]> {
        const queryBuilder = this.jobRepository.createQueryBuilder('job');

        // Apply filters
        if (query.locationType) {
            queryBuilder.andWhere("job.location->>'type' = :locationType", {
                locationType: query.locationType
            });
        }

        if (query.department) {
            queryBuilder.andWhere('job.department = :department', {
                department: query.department
            });
        }

        // Filter by active status if provided
        if (query.isActive !== undefined) {
            queryBuilder.andWhere('job.isActive = :isActive', {
                isActive: query.isActive
            });
        }

        // Search by section title in content sections
        if (query.sectionTitle) {
            queryBuilder.andWhere(`job.contentSections @> '[{"title": :sectionTitle}]'`, {
                sectionTitle: query.sectionTitle
            });
        }

        // Search by keyword across content sections
        if (query.keyword) {
            queryBuilder.andWhere(
                `(job.description ILIKE :keyword OR 
                 job.title ILIKE :keyword OR
                 EXISTS (SELECT 1 FROM jsonb_array_elements(job.contentSections) sections 
                         WHERE sections->>'title' ILIKE :keyword OR 
                         EXISTS (SELECT 1 FROM jsonb_array_elements_text(sections->'content') content 
                                 WHERE content ILIKE :keyword)))`,
                { keyword: `%${query.keyword}%` }
            );
        }

        const sortBy = query.sortBy || 'postedAt';
        const sortOrder = query.sortOrder || 'desc';

        // Handle special case for postedAt sorting
        if (sortBy === 'postedAt') {
            queryBuilder.orderBy('job.postedAt', sortOrder.toUpperCase() as 'ASC' | 'DESC');
        } else {
            queryBuilder.orderBy(`job.${sortBy}`, sortOrder.toUpperCase() as 'ASC' | 'DESC');
        }

        return queryBuilder.getMany();
    }

    async findOne(id: number): Promise<Job> {
        const job = await this.jobRepository.findOne({ where: { id } });
        if (!job) {
            throw new NotFoundException(`Job with ID ${id} not found`);
        }
        return job;
    }


    async findByDepartment(department: string): Promise<Job[]> {
        const jobs = await this.jobRepository.find({ where: { department } });
        if (!jobs || jobs.length === 0) {
            throw new NotFoundException(`No jobs found in department ${department}`);
        }
        return jobs;
    }

    async update(id: number, updateJobDto: UpdateJobDto): Promise<Job> {
        const job = await this.findOne(id);

        // Apply updates
        Object.assign(job, updateJobDto);

        // Update slug if title was changed
        if (updateJobDto.title) {
            job.slug = slugify(updateJobDto.title, { lower: true });
        }

        // Update postedAt if provided
        if (updateJobDto.postedAt) {
            job.postedAt = new Date(updateJobDto.postedAt);
        }

        return this.jobRepository.save(job);
    }

    async remove(id: string): Promise<void> {
        const result = await this.jobRepository.delete(id);
        if (result.affected === 0) {
            throw new NotFoundException(`Job with ID ${id} not found`);
        }
    }

    async getDepartments(): Promise<string[]> {
        const departments = await this.jobRepository
            .createQueryBuilder('job')
            .select('DISTINCT job.department', 'department')
            .orderBy('department', 'ASC')
            .getRawMany();

        return departments.map(item => item.department);
    }
}