import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { Types } from 'mongoose';
import { ModelsService } from 'src/models/models.service';
import { CommonService } from 'src/common/common.service';
import { TranslationService } from 'src/common/services/translation.service';
import { CategoryListDto, CreateCategoryDto, UpdateCategoryDto } from './dto/category.dto';

@Injectable()
export class CategoryService {
    constructor(
        private readonly models: ModelsService,
        private readonly common: CommonService,
        private readonly translationService: TranslationService
    ) { }

    private t(key: string, lang: string) {
        return this.translationService.translate(key, lang);
    }

    private toObjectId(id: string, lang: string) {
        if (!Types.ObjectId.isValid(id)) {
            throw new HttpException({ message: this.t('CATEGORY_NOT_FOUND', lang) }, HttpStatus.NOT_FOUND);
        }
        return new Types.ObjectId(id);
    }

    private async assertNameFree(name: string, lang: string, excludeId?: Types.ObjectId) {
        const filter: any = { name };
        if (excludeId) filter._id = { $ne: excludeId };
        const existing = await this.models.CategoryModel.findOne(filter).collation({ locale: 'en', strength: 2 }).lean();
        if (existing) {
            throw new HttpException({ message: this.t('CATEGORY_ALREADY_EXISTS', lang) }, HttpStatus.BAD_REQUEST);
        }
    }

    async create(dto: CreateCategoryDto, lang: string) {
        await this.assertNameFree(dto.name, lang);
        const category = await this.models.CategoryModel.create({ name: dto.name });
        return this.common.successResponse(this.t('CATEGORY_CREATED', lang), category);
    }

    async list(query: CategoryListDto, lang: string) {
        const { page, limit, search, is_active } = query;
        const filter: any = {};
        if (search) filter.name = { $regex: search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), $options: 'i' };
        if (is_active !== undefined) filter.is_active = is_active === 'true';

        const [total, categories] = await Promise.all([
            this.models.CategoryModel.countDocuments(filter),
            this.models.CategoryModel.find(filter)
                .sort({ created_at: 1 })
                .skip((page - 1) * limit)
                .limit(limit)
                .lean(),
        ]);

        return this.common.paginatedResponse(this.t('CATEGORIES_FETCHED', lang), categories, total, page, limit);
    }

    async getById(id: string, lang: string) {
        const category = await this.models.CategoryModel.findById(this.toObjectId(id, lang)).lean();
        if (!category) {
            throw new HttpException({ message: this.t('CATEGORY_NOT_FOUND', lang) }, HttpStatus.NOT_FOUND);
        }
        return this.common.successResponse(this.t('CATEGORY_FETCHED', lang), category);
    }

    async update(id: string, dto: UpdateCategoryDto, lang: string) {
        const _id = this.toObjectId(id, lang);
        const category = await this.models.CategoryModel.findById(_id);
        if (!category) {
            throw new HttpException({ message: this.t('CATEGORY_NOT_FOUND', lang) }, HttpStatus.NOT_FOUND);
        }

        if (dto.name !== undefined && dto.name.toLowerCase() !== category.name.toLowerCase()) {
            await this.assertNameFree(dto.name, lang, _id);
        }
        if (dto.name !== undefined) category.name = dto.name;
        if (dto.is_active !== undefined) category.is_active = dto.is_active;
        await category.save();

        return this.common.successResponse(this.t('CATEGORY_UPDATED', lang), category);
    }
}
