import { TestimonialDao } from "../dao/testimonial.dao";
import { LanguageService } from "../services/language.service";
import { sequelize } from "../models";
import { Common } from "../../utils/common";
import { AppError } from "../../utils/errors";
import { TESTIMONIAL } from "../config/constants";

export class TestimonialService {
    private accountId: number | null;
    private userId: number | null;
    private language: string;
    private testimonialDao: TestimonialDao;
    private languageService: LanguageService;
    
    constructor(
        private options: {
            language: string;
            accountId: number | null;
            userId: number | null;
        } = {
                language: process.env.DEFAULT_LANGUAGE_CODE!,
                userId: null,
                accountId: null,
            }
    ) {
        this.language = options.language;
        this.userId = options.userId ?? null;
        this.accountId = options.accountId ?? null;
        this.testimonialDao = new TestimonialDao({
            userId: this.userId,
            accountId: this.accountId,
            language: this.language,
        });
        this.languageService = new LanguageService({
            userId: this.userId,
            accountId: this.accountId,
            language: this.language,
            scope: null,
            config: null
        });
    }

    // create a new testimonial
    createTestimonial = async (data: TestimonialCreateServiceInput): Promise<testimonialObjectInteface> => {
        const transaction = await sequelize.transaction();
        try {
            const { name, designation, content, imageId, status } = data;
            const languages: LanguageInfo = await this.languageService.getRequestedAndDefaultLanguage();
            let testimonialObj: testimonialObject = { name, designation, imageId, status: status ?? TESTIMONIAL.STATUS.ACTIVE };
            let testimonialContentObj: testimonialContentObject = { content };
            
            const createTestimonialInput: TestimonialCreateDaoInput = { testimonialObj, testimonialContentObj, languages };
            let testimonialIdentifier: number = await this.testimonialDao.create(createTestimonialInput, { transaction });
            
            const getTestimonialByIdInput: TestimonialGetByIdDaoInput = { id: testimonialIdentifier };
            let testimonial: testimonialObjectInteface = await this.testimonialDao.getTestimonialById(getTestimonialByIdInput, { transaction });
            await transaction.commit();
            return testimonial;
        } catch (err) {
            await transaction.rollback();
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // update the existing testimonial
    updateTestimonial = async (data: TestimonialUpdateServiceInput): Promise<testimonialObjectInteface> => {
        const transaction = await sequelize.transaction();
        try {
            const { id, name, designation, content, imageId, status } = data;
            const languages: LanguageInfo = await this.languageService.getRequestedLanguage();
            let testimonialObj: testimonialObject = { name, designation, imageId, status: status ?? TESTIMONIAL.STATUS.ACTIVE };
            let testimonialContentObj: testimonialContentObject = { content };
            
            const exists = await this.testimonialDao.doExistsById({ id }, { transaction });
            if (exists) {
                const updateTestimonialInput: TestimonialUpdateDaoInput = { id, testimonialObj, testimonialContentObj, languages };
                await this.testimonialDao.update(updateTestimonialInput, { transaction });
                const getTestimonialByIdInput: TestimonialGetByIdDaoInput = { id };
                let testimonial = await this.testimonialDao.getTestimonialById(getTestimonialByIdInput, { transaction });
                await transaction.commit();
                return testimonial;
            }
            throw new AppError(404, 'TESTIMONIAL_NOT_FOUND', { name: 'TESTIMONIAL_NOT_FOUND' });
        } catch (err) {
            await transaction.rollback();
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // delete testimonial
    deleteTestimonial = async (data: TestimonialDeleteServiceInput): Promise<testimonialObjectInteface> => {
        const transaction = await sequelize.transaction();
        try {
            const { id } = data;
            const exists = await this.testimonialDao.doExistsById({ id }, { transaction });
            if (exists) {
                const getTestimonialByIdInput: TestimonialGetByIdDaoInput = { id, paranoid: true };
                let testimonial = await this.testimonialDao.getTestimonialById(getTestimonialByIdInput, { transaction });
                const deleteTestimonialInput: TestimonialDeleteDaoInput = { id };
                await this.testimonialDao.delete(deleteTestimonialInput, { transaction });
                await transaction.commit();
                return testimonial;
            }
            throw new AppError(404, 'TESTIMONIAL_NOT_FOUND', { name: 'TESTIMONIAL_NOT_FOUND' });
        } catch (err) {
            await transaction.rollback();
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // get by id
    getById = async (data: TestimonialGetByIdServiceInput): Promise<testimonialObjectInteface> => {
        try {
            const getTestimonialByIdInput: TestimonialGetByIdDaoInput = { id: data.id };
            let testimonial = await this.testimonialDao.getTestimonialById(getTestimonialByIdInput);
            if (testimonial) {
                return testimonial;
            }
            throw new AppError(404, 'TESTIMONIAL_NOT_FOUND', { name: 'TESTIMONIAL_NOT_FOUND' });
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // list testimonials
    getTestimonials = async (data: TestimonialGetTestimonialsServiceInput): Promise<TestimonialPaginatedList> => {
        try {
            const { listRequest, status = null } = data;
            const { page, perPage } = listRequest;
            if (status) {
                listRequest.status = status;
            }
            const getTestimonialListInput: TestimonialGetTestimonialListDaoInput = { listRequest };
            let testimonials: TestimonialPaginatedData = await this.testimonialDao.getTestimonialList(getTestimonialListInput);
            if (testimonials) {
                let totalPages = Common.getTotalPages(testimonials.count, perPage);
                return {
                    data: testimonials.rows,
                    page: page,
                    perPage: perPage,
                    totalRecords: testimonials.count,
                    totalPages: totalPages
                } as unknown as TestimonialPaginatedList;
            }
            throw new AppError(400, 'TESTIMONIAL_LIST_ERROR_OUT', {});
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // list all testimonials
    getAllTestimonials = async (data: TestimonialGetAllTestimonialsServiceInput): Promise<testimonialObjectSummaryInteface[]> => {
        try {
            const { listRequest } = data;
            const getAllTestimonialsInput: TestimonialGetAllTestimonialsDaoInput = { listRequest };
            let testimonials = await this.testimonialDao.getAllTestimonials(getAllTestimonialsInput);
            if (testimonials) {
                return testimonials;
            }
            throw new AppError(400, 'TESTIMONIAL_LIST_ERROR_OUT', {});
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // update status
    updateStatus = async (data: TestimonialUpdateStatusDaoInput): Promise<testimonialObjectInteface> => {
        try {
            const getTestimonialByIdInput: TestimonialGetByIdDaoInput = { id: data.id };
            let testimonial = await this.testimonialDao.getTestimonialById(getTestimonialByIdInput);
            if (testimonial) {
                const updateTestimonialStatusInput: TestimonialUpdateStatusDaoInput = { id: data.id, status: data.status };
                await this.testimonialDao.updateStatus(updateTestimonialStatusInput);
                let testimonialObject = await this.getById(getTestimonialByIdInput);
                return testimonialObject;
            } else {
                throw new AppError(404, 'TESTIMONIAL_NOT_FOUND', { id: 'TESTIMONIAL_NOT_FOUND' });
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }
}
