import { BaseModel as LucidBaseModel } from '@ioc:Adonis/Lucid/Orm';
// import { ManyToManyQueryClient } from '@ioc:Adonis/Lucid/Orm';

// export class CustomManyToManyQueryClient extends ManyToManyQueryClient {
//   public attach(
//     relatedIds: any | any[],
//     pivotAttributes: any = {},
//     trx?: ReturnType<typeof this.model.transaction>
//   ) {
//     return super.attach(relatedIds, (row) => {
//       row.pivot.fill(pivotAttributes);
//     }, trx);
//   }
// }

/**
 * Helper to find if value is a valid Object or
 * not
 */
export function isObject(value: any): boolean {
    return value !== null && typeof value === 'object' && !Array.isArray(value);
}

export default class BaseModel extends LucidBaseModel {
    /**
     * When `fill` method is called, then we may have a situation where it
     * removed the values which exists in `original` and hence the dirty
     * diff has to do a negative diff as well
     */
    // private fillInvoked: boolean = false;

    public static fillable: string[] = [];

    public fill(attributes: any, allowExtraProperties: boolean = false): this {
        this.$attributes = {};
        // const Model = this.constructor as typeof BaseModel;

        // for (const key in attributes) {
        //     if (Model.fillable.includes(key)) {
        //         const value = attributes[key];
        //         if (Model.$hasColumn(key)) {
        //             this[key] = value;
        //         }
        //     }
        // }
        this.mergeFillableAttributes(attributes, allowExtraProperties);
        // this.fillInvoked = true;
        return this;
    }

    /**
     * Merge bulk attributes with existing attributes.
     *
     * 1. If key is unknown, it will be added to the `extras` object.
     * 2. If key is defined as a relationship, it will be ignored and one must call `$setRelated`.
     */
    public mergeFillableAttributes(values: any, allowExtraProperties: boolean = false): this {
        const Model = this.constructor as typeof BaseModel;

        /**
         * Merge values with the attributes
         */
        if (isObject(values)) {
            // Object.keys(values).forEach((key) => {
            for (const key in values) {
                if (Model.fillable.includes(key)) {
                    const value = values[key];

                    /**
                     * Set as column
                     */
                    if (Model.$hasColumn(key)) {
                        this[key] = value;
                        continue;
                    }

                    /**
                     * Resolve the attribute name from the column names. Since people
                     * usaully define the column names directly as well by
                     * accepting them directly from the API.
                     */
                    const attributeName = Model.$keys.columnsToAttributes.get(key);
                    if (attributeName) {
                        this[attributeName] = value;
                        continue;
                    }

                    /**
                     * If key is defined as a relation, then ignore it, since one
                     * must pass a qualified model to `this.$setRelated()`
                     */
                    if (Model.$relationsDefinitions.has(key)) {
                        continue;
                    }

                    /**
                     * If the property already exists on the model, then set it
                     * as it is vs defining it as an extra property
                     */
                    if (this.hasOwnProperty(key)) {
                        this[key] = value;
                        continue;
                    }

                    /**
                     * Raise error when not instructed to ignore non-existing properties.
                     */
                    if (!allowExtraProperties) {
                        throw new Error(`Cannot define "${key}" on "${Model.name}" model, since it is not defined as a model property`);
                    }

                    this.$extras[key] = value;
                }
            }
        }

        return this;
    }
}
