const db = require('../models');
const FileDBApi = require('./file');
const crypto = require('crypto');
const Utils = require('../utils');

const Sequelize = db.Sequelize;
const Op = Sequelize.Op;

module.exports = class LeadsDBApi {
    static async create(data, options) {
        const currentUser = (options && options.currentUser) || { id: null };
        const transaction = (options && options.transaction) || undefined;

        const leads = await db.leads.create(
            {
                id: data.id || undefined,
                keyword: data.keyword || null,
                description: data.description || null,
                urgency: data.urgency || null,
                status: data.status || 'SUBMITTED',
                contact_name: data.contact_name || null,
                contact_phone: data.contact_phone || null,
                contact_email: data.contact_email || null,
                address: data.address || null,
                city: data.city || null,
                state: data.state || null,
                zip: data.zip || null,
                lat: data.lat || null,
                lng: data.lng || null,
                inferred_tags_json: data.inferred_tags_json || null,
                tenant_key: data.tenant_key || null,
                created_at_ts: data.created_at_ts || null,
                updated_at_ts: data.updated_at_ts || null,
                importHash: data.importHash || null,
                createdById: currentUser.id,
                updatedById: currentUser.id,
            },
            { transaction },
        );

        await leads.setUser( data.user || currentUser.id, { transaction });
        await leads.setCategory( data.category || null, { transaction });

        return leads;
    }

    static async findAll(filter, options) {
        const limit = filter.limit || 0;
        let offset = 0;
        let where = {};
        const currentPage = +filter.page;
        offset = currentPage * limit;

        const currentUser = options?.currentUser;
        const transaction = (options && options.transaction) || undefined;

        // Data Isolation
        if (currentUser && currentUser.app_role) {
            const roleName = currentUser.app_role.name;
            if (roleName === 'Consumer') {
                where.userId = currentUser.id;
            } else if (roleName === 'Verified Business Owner') {
                // Business owners see leads matched to them
                if (currentUser.businessId) {
                    where['$lead_matches_lead.businessId$'] = currentUser.businessId;
                } else {
                    where['$lead_matches_lead.business.owner_userId$'] = currentUser.id;
                }
            }
        }

        let include = [
            { model: db.users, as: 'user' },
            { model: db.categories, as: 'category' },
            { 
                model: db.lead_matches, 
                as: 'lead_matches_lead',
                include: [{ model: db.businesses, as: 'business' }]
            }
        ];

        // Apply filters
        if (filter) {
            if (filter.id) where.id = Utils.uuid(filter.id);
            if (filter.keyword) where.keyword = { [Op.iLike]: `%${filter.keyword}%` };
            if (filter.status) where.status = filter.status;
        }

        const queryOptions = {
            where,
            include,
            distinct: true,
            subQuery: false,
            limit: options?.countOnly ? undefined : (limit ? Number(limit) : undefined),
            offset: options?.countOnly ? undefined : (offset ? Number(offset) : undefined),
            order: [['createdAt', 'desc']],
            transaction
        };

        const { rows, count } = await db.leads.findAndCountAll(queryOptions);

        return {
            rows: options?.countOnly ? [] : rows,
            count: count
        };
    }

    static async findAllAutocomplete(query, limit, offset) {
        let where = {};
        if (query) {
            where = {
                [Op.or]: [
                    { ['id']: Utils.uuid(query) },
                    { ['keyword']: { [Op.iLike]: `%${query}%` } },
                ],
            };
        }

        const records = await db.leads.findAll({
            attributes: ['id', 'keyword'],
            where,
            limit: limit ? Number(limit) : undefined,
            offset: offset ? Number(offset) : undefined,
            order: [['keyword', 'ASC']],
        });

        return records.map((record) => ({
            id: record.id,
            label: record.keyword,
        }));
    }

    static async findBy(where, options) {
        const transaction = (options && options.transaction) || undefined;
        const leads = await db.leads.findOne({ 
            where,
            include: [
                { model: db.users, as: 'user' },
                { model: db.categories, as: 'category' },
                { 
                    model: db.lead_matches, 
                    as: 'lead_matches_lead',
                    include: [{ model: db.businesses, as: 'business' }]
                },
                { model: db.messages, as: 'messages_lead' },
                { 
                  model: db.lead_photos, 
                  as: 'lead_photos_lead',
                  include: [{ model: db.file, as: 'photos' }]
                },
                { 
                  model: db.lead_events, 
                  as: 'lead_events_lead',
                  include: [{ model: db.users, as: 'actor_user' }]
                }
            ],
            transaction 
        });
        return leads ? leads.get({plain: true}) : null;
    }

    static async update(id, data, options) {
        const currentUser = (options && options.currentUser) || {id: null};
        const transaction = (options && options.transaction) || undefined;
        const leads = await db.leads.findByPk(id, {transaction});
        if (!leads) return null;

        const updatePayload = { ...data, updatedById: currentUser.id };
        await leads.update(updatePayload, {transaction});

        if (data.user) await leads.setUser(data.user, { transaction });
        if (data.category) await leads.setCategory(data.category, { transaction });

        return leads;
    }

    static async remove(id, options) {
        const currentUser = (options && options.currentUser) || {id: null};
        const transaction = (options && options.transaction) || undefined;
        const leads = await db.leads.findByPk(id, options);
        await leads.update({ deletedBy: currentUser.id }, { transaction });
        await leads.destroy({ transaction });
        return leads;
    }

    static async deleteByIds(ids, options) {
        const currentUser = (options && options.currentUser) || { id: null };
        const transaction = (options && options.transaction) || undefined;
        const leads = await db.leads.findAll({ where: { id: { [Op.in]: ids } }, transaction });
        for (const record of leads) {
            await record.update({deletedBy: currentUser.id}, {transaction});
            await record.destroy({transaction});
        }
        return leads;
    }
};