import { Schema, model, Model, FilterQuery } from "mongoose";
import {
    CreatePostParams,
    DeletePostParams,
    FindPostsParams,
    IPost,
    IPostAdapter,
    IPostDocument,
    PaginatePostsParams,
    PaginatePostsResults,
    UpdatePostParams,
} from "./Posts.Interface";
import { getAuthorizedUsers } from "../../helpers/check";
import CompaniesList from "../Companies/Companies";
export class PostsAdapterMongoose implements IPostAdapter {
    schema: Schema;
    postList: Model<IPostDocument>;

    constructor() {
        this.schema = new Schema({
            companyId: {
                type: Schema.Types.ObjectId,
                required: true,
                ref: "Company",
            },
            content: { type: String, required: true },
            fixed: { type: Boolean, required: false },
            fixedOrder: { type: Number, required: false },
            createdAt: { type: Date, default: Date.now, required: true },
        });
        this.postList = model<IPostDocument>("Post", this.schema);
    }

    public async create(data: Omit<CreatePostParams, "sessionUser">): Promise<IPost> {
        return await this.postList.create(data);
    }

    public async update(data: Omit<UpdatePostParams, "sessionUser">): Promise<IPost> {
        await this.postList
            .updateOne(
                { _id: data.postId },
                {
                    content: data.content,
                    fixed: data.fixed,
                    fixedOrder: data.fixedOrder,
                }
            )
            .exec();

        const updatedPost = await this.postList.findById(data.postId).exec();
        if (!updatedPost) {
            throw new Error("Error al actualizar el post");
        }

        return updatedPost;
    }

    public async delete(data: Omit<DeletePostParams, "sessionUser">): Promise<void> {
        await this.postList.deleteOne({ _id: data.id });
    }

    public async find(filters: FindPostsParams): Promise<IPost[]> {
        const searchCriteria = this.buildSearchCriteria(filters);
        return await this.postList.find(searchCriteria).exec();
    }

    public async findOne(filters: FindPostsParams): Promise<IPostDocument | null> {
        const searchCriteria = this.buildSearchCriteria(filters);
        return await this.postList.findOne(searchCriteria).exec();
    }

    private buildSearchCriteria(filters: FindPostsParams): FilterQuery<IPost> {
        const searchCriteria: FilterQuery<IPost> = {};

        if (filters.id) searchCriteria._id = filters.id;
        if (filters.companyId) searchCriteria.companyId = filters.companyId;
        if (filters.fixed) searchCriteria.fixed = filters.fixed;

        return searchCriteria;
    }

    public async paginate(filters: PaginatePostsParams): Promise<PaginatePostsResults> {
        const searchCriteria = this.buildSearchCriteria(filters);
        const countUnfixed = await this.postList.countDocuments(searchCriteria).exec();
        //const skip = (filters.page - 1) * filters.limit;

        //Cargar los ids fijos.
        const company = await CompaniesList.companies.findOne({
            _id: searchCriteria.companyId,
        });

        const fixedPostIds = company?.fixedPostIds || [];
        //const countFixed = fixedPostIds.length;
        //const count = countUnfixed + countFixed;

        const fixedPosts: Array<IPost> = [];

        for (const postId of fixedPostIds) {
            const post = await this.postList.findOne({ _id: postId });
            if (post) {
                fixedPosts.push(post as IPost);
            }
        }

        //searchCriteria._id = { $nin: fixedPostIds };

        const resultsUnfixed = await this.postList
            .find(searchCriteria)
            //.skip(skip)
            //.limit(filters.limit)
            .sort({ createdAt: -1 })
            .exec();

        //quitar de resultsUnfixed los que esten en fixedPostIds
        const filteredResultsUnfixed = resultsUnfixed.filter(
            (post) => !fixedPostIds.includes(String(post._id))
        );

        const results: Array<IPost> = [...fixedPosts, ...filteredResultsUnfixed];

        //obtengo la porcion de posts correspondiente a la pagina
        const startIndex = (filters.page - 1) * filters.limit;
        const endIndex = startIndex + filters.limit;
        const paginatedResults = results.slice(startIndex, endIndex);

        let pages = Math.ceil(countUnfixed / filters.limit);
        if (pages == 0) {
            pages = 1;
        }

        let authorizedUsers: string[] = [];

        if (searchCriteria.companyId) {
            const result = await getAuthorizedUsers({
                companyId: String(searchCriteria.companyId),
            });

            authorizedUsers = result.authorizedUsers;
        }

        return { data: paginatedResults, page: filters.page, pages: pages, authorizedUsers };
    }
}
