import {
    IUsersAdapter,
    IUser,
    FindUsersParams,
    UpdateUserParams,
    PaginateUsersParams,
    PaginateUsersResults,
    SysAdminPaginateUsersParams,
} from "./Users.Interface";
import { Schema, Document, Model, model } from "mongoose";

export interface IUserDocument extends Omit<IUser, "id">, Document {}

export class UsersMongooseAdapter implements IUsersAdapter {
    schema: Schema;
    userList: Model<IUserDocument>;

    constructor() {
        this.schema = new Schema({
            email: { type: String, required: true, unique: true },
            password: { type: String, required: false },
            lastName: { type: String, required: false },
            firstName: { type: String, required: false },
            external_id: { type: String, required: false },
            external_service: { type: String, required: false }, //google, facebook
            street: { type: String, required: false },
            streetNumber: { type: String, required: false },
            builingFloor: { type: String, required: false },
            buildingApartament: { type: String, required: false },
            block: { type: String, required: false },
            city: { type: String, required: false },
            state: { type: String, required: false },
            country: { type: String, required: false },
            zipCode: { type: String, required: false },
            phoneCountryCode: { type: String, required: false },
            phoneAreaCode: { type: String, required: false },
            phoneNumber: { type: String, required: false },
            creationDate: { type: Date, required: true, default: Date.now },
            lastLoginDate: { type: Date, default: Date.now },
            verificated: { type: Boolean, required: false, default: false },
            verificationCode: { type: String, required: false },
            recoveryCode: { type: String, required: false },
            avatar: { type: String, required: false },
            mpPayerEmail: { type: String, required: false },
        });

        this.userList = model<IUserDocument>("User", this.schema);
    }

    public async create(user: IUser): Promise<IUser> {
        const newUser = await this.userList.create(user);
        return newUser;
    }

    public async delete(id: string): Promise<void> {
        await this.userList.deleteOne({ _id: id });
    }

    public async find(filters: FindUsersParams): Promise<IUser[]> {
        return await this.userList.find(filters).exec();
    }

    public async findOne(filters: FindUsersParams): Promise<IUserDocument | null> {
        return await this.userList.findOne(filters).exec();
    }

    public async paginate(filters: PaginateUsersParams): Promise<PaginateUsersResults> {
        const { page, limit, ...findFilters } = filters; //Extract page and limit from filters
        const count = await this.userList.countDocuments(findFilters).exec();
        const skip = (page - 1) * limit;

        const results = await this.userList
            .find(findFilters)
            .skip(skip)
            .limit(filters.limit)
            .exec();

        return {
            data: results,
            page: filters.page,
            pages: Math.ceil(count / filters.limit),
        };
    }

    public async sysAdminPaginate(filters: SysAdminPaginateUsersParams): Promise<PaginateUsersResults> {
        const { page, limit, sortBy, sortOrder, name, lastName, email, isVerified, createdFrom, createdTo } = filters;
        
        const query: any = {};
        
        if (name) {
            query.firstName = { $regex: name, $options: "i" };
        }
        if (lastName) {
            query.lastName = { $regex: lastName, $options: "i" };
        }
        if (email) {
            query.email = { $regex: email, $options: "i" };
        }
        if (isVerified !== undefined) {
            query.verificated = isVerified;
        }
        
        if (createdFrom || createdTo) {
            query.creationDate = {};
            if (createdFrom) query.creationDate.$gte = new Date(createdFrom);
            if (createdTo) query.creationDate.$lte = new Date(createdTo);
        }

        const count = await this.userList.countDocuments(query).exec();
        const skip = (page - 1) * limit;

        const sortObj: any = {};
        if (sortBy) {
            const order = sortOrder === "desc" ? -1 : 1;
            if (sortBy === "name") {
                sortObj.firstName = order;
                sortObj.lastName = order;
            } else if (sortBy === "createdAt") {
                sortObj.creationDate = order;
            }
        } else {
            sortObj.creationDate = -1; // Default
        }

        const results = await this.userList
            .find(query)
            .sort(sortObj)
            .skip(skip)
            .limit(limit)
            .exec();

        return {
            data: results,
            page,
            pages: Math.ceil(count / limit),
        };
    }


    public async update(data: UpdateUserParams): Promise<void> {
        const updateUser = await this.userList.findOne({ _id: data.id });

        if (!updateUser) {
            throw new Error("User not found");
        }

        if (data.firstName) {
            updateUser.firstName = data.firstName;
        }

        if (data.lastName) {
            updateUser.lastName = data.lastName;
        }

        if (data.street) {
            updateUser.street = data.street;
        }

        if (data.streetNumber) {
            updateUser.streetNumber = data.streetNumber;
        }

        if (data.builingFloor) {
            updateUser.builingFloor = data.builingFloor;
        }

        if (data.buildingApartament) {
            updateUser.buildingApartament = data.buildingApartament;
        }

        if (data.block) {
            updateUser.block = data.block;
        }

        if (data.city) {
            updateUser.city = data.city;
        }

        if (data.state) {
            updateUser.state = data.state;
        }

        if (data.country) {
            updateUser.country = data.country;
        }

        if (data.zipCode) {
            updateUser.zipCode = data.zipCode;
        }

        if (data.phoneCountryCode) {
            updateUser.phoneCountryCode = data.phoneCountryCode;
        }

        if (data.phoneAreaCode) {
            updateUser.phoneAreaCode = data.phoneAreaCode;
        }

        if (data.phoneNumber) {
            updateUser.phoneNumber = data.phoneNumber;
        }

        updateUser.save();
    }
}
