
import Redis from 'ioredis'
import { List } from 'whatsapp-web.js'
const unflatten = require('flat').unflatten
var flatten = require('flat')
import ListTicketServiceCache from "../services/TicketServices/ListTicketServiceCache"

import { escapeCharCache } from './ContactsCache'

let redis: any = null
  
if (process.env.CACHE) {   
    redis = new Redis();
}


const redisConn = async () => {

    if (!process.env.CACHE) {
        return null
    }

    // try {

    //     const redis = new Redis();

    //     const conn = () => new Promise((resolve, reject) => {

    //         redis.on('error', async (err) => {

    //             if (err.code === 'ECONNREFUSED') {
    //                 console.error(`Redis connection error: ${err}.`)
    //                 // await redis.quit()
    //             }
    //             else {
    //                 console.error(`Redis encountered an error: ${err.message}.`)
    //             }

    //             reject(err)

    //         })

    //         redis.on('connect', () => {

    //             // console.log('CacheStore - Connection status: connected');
    //             resolve(redis);

    //         })

    //         redis.on('end', () => {
    //             // console.log('CacheStore - Connection status: disconnected');
    //         });

    //         redis.on('reconnecting', () => {
    //             // console.log('CacheStore - Connection status: reconnecting');
    //         });
    //     });

    //     return await conn();

    // } catch (e) {
    //     console.error(`There whas an error on redis connection: ${e}`);
    //     return Promise.resolve([]);
    // }

    return redis

}


const flushCache = async () => {

    const redis: any = await redisConn();

    if (!redis) return false

    // if (redis.status === 'connect') {

    //     console.log('TICKETS CACHE REMOVED')
    //     await redis.call('FLUSHALL') 
    // }


    await redis.call('FLUSHALL')

}

const cacheSize = async () => {

    const redis: any = await redisConn();

    if (!redis) return null

    // if (redis.status !== 'connect') return -1

    const size = await redis.call('dbsize')
    // await redis.quit()
    return size

}

const loadTicketsCache = async () => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    await createTicketIndexCache('idx_ticket')

    let tickets = await ListTicketServiceCache({})

    const pipeline = redis.pipeline()

    for (let i = 0; i < tickets.length; i++) {

        tickets[i].createdAt = new Date(tickets[i].createdAt).toISOString()
        tickets[i].updatedAt = new Date(tickets[i].updatedAt).toISOString()

        tickets[i].escaped_name = escapeCharCache(tickets[i]['contact.name'])

        // tickets[i]['contact_name'] = tickets[i]['contact.name']
        // delete tickets[i]['contact.name']

        tickets[i]['contact_number'] = tickets[i]['contact.number']
        delete tickets[i]['contact.number']

        pipeline.hmset(`ticket:${tickets[i].id}`, tickets[i]);
    }

    await pipeline.exec(() => { console.log(`${tickets.length} TICKETS INSERTED IN CACHE!`) });

    // await redis.quit()
}


const createTicketIndexCache = async (hashIndex: string) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    try {

        const lst_index_redis: any = await redis.call('FT._LIST')

        if (lst_index_redis.includes(hashIndex)) {
            console.log('entrou...')
            await redis.call('FT.DROPINDEX', hashIndex)
        }

        const response = await redis.call('FT.CREATE', hashIndex, 'ON', 'HASH', 'PREFIX', '1', 'ticket:', 'SCHEMA', 'escaped_name', 'TEXT', 'SORTABLE', 'contact_number', 'TEXT', 'SORTABLE', 'status', 'TAG', 'SORTABLE')

        console.log('Ticket index created: ', response)

    } catch (error) {
        console.log('There was an error on createTicketIndexCache: ', error)
    }

    // await redis.quit()
}

const updateTicketCache = async (hash: any, json_object: any) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    const pipeline = redis.pipeline()
    let entries = Object.entries(json_object)

    entries.forEach((e: any) => {
        pipeline.hset(hash, e[0], e[1])
    })

    await pipeline.exec(() => { console.log("updateTicketCache Key/value inserted/updated") });

    // await redis.quit()

}

const updateTicketCacheByTicketId = async (ticketId: string | number, update_fields: any) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    const ticket_cache = await redis.hgetall(`ticket:${ticketId}`)

    try {
        if (ticket_cache && Object.keys(ticket_cache).length > 0) {

            if (update_fields.escaped_name) {
                update_fields.escaped_name = escapeCharCache(update_fields['contact.name'])
            }

            await updateTicketCache(`ticket:${ticketId}`, update_fields)

            console.log(`updateTicketCacheByTicketId TICKET ${ticket_cache['contact_number']} CACHE WAS UPDATED!`)
        }
        else {
            console.log('TICKET CACHE NOT FOUND!')
        }
    } catch (error) {
        console.log(`There was an error on updateTicketCacheByTicketId:  ${error}`)
    }

    // await redis.quit()


}


const createOrUpdateTicketCache = async (hash: any, ticket: any) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return 

    ticket.escaped_name = escapeCharCache(ticket['contact.name'])

    ticket['contact_number'] = ticket['contact.number']
    delete ticket['contact.number']

    await redis.hmset(hash, ticket);

    console.log('CREATED/UPDATED TICKET CACHE')

    // await redis.quit()

}

const deleteTicketsByIdCache = async (ticketId: string | number) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    const ticket_cache = await redis.hgetall(`ticket:${ticketId}`)

    try {
        if (ticket_cache && Object.keys(ticket_cache).length > 0) {

            await redis.del(`ticket:${ticketId}`)

            console.log(`TICKET ${ticket_cache['id']} CACHE WAS DELETED!`)
        }
        else {
            console.log('TICKET CACHE NOT FOUND!')
        }
    } catch (error) {
        console.log(`There was an error on deleteTicketsByIdCache:  ${error}`)
    }

    // await redis.quit()

}

const deleteTicketsFieldsCache = async (tickets: any, del_fields: any) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    const pipeline = redis.pipeline()

    if (tickets && tickets.length > 0) {
        try {

            for (let i = 0; i < tickets.length; i++) {

                pipeline.hdel(`ticket:${tickets[i]['id']}`, del_fields)

            }

            await pipeline.exec(() => { console.log(`Tickets cache contact updated!`) })

        } catch (error) {
            console.log('There was an error on deleteTicketsFieldsByContactsCache function: ', error)
        }

    }

    // await redis.quit()

}


const updateTicketsByContactsCache = async (oldNumber: string, newName: string, newNumber: string) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    const pipeline = redis.pipeline()

    const tickets = await searchTicketCache(oldNumber)

    if (tickets && tickets.length > 0) {
        try {
            for (let i = 0; i < tickets.length; i++) {

                tickets[i]['contact.name'] = newName
                tickets[i].escaped_name = escapeCharCache(newName)
                tickets[i]['contact_number'] = newNumber

                pipeline.hmset(`ticket:${tickets[i]['id']}`, tickets[i])

            }

            await pipeline.exec(() => { console.log(`updateTicketsByContactsCache Tickets cache contact updated!`) })

        } catch (error) {
            console.log('There was an error on updateTicketsByContactsCache function: ', error)
        }

    }

    // await redis.quit()

}


const deleteTicketsByContactsCache = async (number: string) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    const pipeline = redis.pipeline()

    const tickets = await searchTicketCache(number)

    if (tickets && tickets.length > 0) {
        try {
            for (let i = 0; i < tickets.length; i++) {

                pipeline.del(`ticket:${tickets[i]['id']}`)

            }

            await pipeline.exec(() => { console.log(`Tickets cache number ${tickets[0]['contact_number']} deleted!`) })

        } catch (error) {
            console.log('There was an error on deleteTicketsByContactsCache function: ', error)
        }

    }

    // await redis.quit()

}

const deleteTicketCache = async (hash: any) => {

    const redis: any = await redisConn();

    if (!redis) return

    //  if (redis.status !== 'connect') return

    await redis.del(hash)
    // await redis.quit()

}

async function searchTicketCache(search: string, offset?: number, limit?: number) {

    const redis: any = await redisConn();

    if (!redis) return

    // if(redis.status!=='connect') return null 

    search = escapeCharCache(search)

    let response: any = undefined

    if (offset != undefined && limit != undefined) {
        response = await redis.call('FT.SEARCH', 'idx_ticket', `(@escaped_name:*${search}*)|(@contact_number:*${search}*)`, 'LIMIT', offset, limit, 'SORTBY', 'status', 'DESC')
    }
    else {
        response = await redis.call('FT.SEARCH', 'idx_ticket', `(@escaped_name:*${search}*)|(@contact_number:*${search}*)`)
    }
    // await redis.quit()

    // console.log('response: ', response)

    if (response.length === 1) {
        return []
    }

    const results: any = []

    for (let n = 2; n < response.length; n += 2) {
        const result: any = {}
        const fieldNamesAndValues = response[n]

        for (let m = 0; m < fieldNamesAndValues.length; m += 2) {
            const k = fieldNamesAndValues[m]
            const v = fieldNamesAndValues[m + 1]
            result[k] = v
        }

        results.push(result)
    }

    return results
}

export {
    loadTicketsCache,
    updateTicketCache,
    createOrUpdateTicketCache,
    searchTicketCache,
    updateTicketCacheByTicketId,
    deleteTicketsByContactsCache,
    updateTicketsByContactsCache,
    deleteTicketsByIdCache,
    flushCache,
    deleteTicketsFieldsCache,
    cacheSize,
    redisConn
}