import axios from "axios"
import dayjs from "dayjs"
import { ImapFlow } from "imapflow"
import { simpleParser } from "mailparser"
import { FastifyInstance } from "fastify"

import {saveFile} from "../../utils/files";
import { secrets } from "../../utils/secrets"

// Drizzle Imports
import {
    tenants,
    folders,
    filetags,
} from "../../../db/schema"

import {
    eq,
    and,
} from "drizzle-orm"


export function syncDokuboxService (server: FastifyInstance)  {
    let badMessageDetected = false
    let badMessageMessageSent = false

    let client: ImapFlow | null = null

    async function initDokuboxClient() {
        if (client?.usable) {
            return client
        }

        client = new ImapFlow({
            host: secrets.DOKUBOX_IMAP_HOST,
            port: secrets.DOKUBOX_IMAP_PORT,
            secure: secrets.DOKUBOX_IMAP_SECURE,
            auth: {
                user: secrets.DOKUBOX_IMAP_USER,
                pass: secrets.DOKUBOX_IMAP_PASSWORD
            },
            logger: false
        })

        console.log("Dokubox E-Mail Client Initialized")

        await client.connect()
        return client
    }

    const syncDokubox = async () => {

            console.log("Perform Dokubox Sync")

            await initDokuboxClient()

            if (!client?.usable) {
                throw new Error("E-Mail Client not usable")
            }

            // -------------------------------
            // TENANTS LADEN (DRIZZLE)
            // -------------------------------
            const tenantList = await server.db
                .select({
                    id: tenants.id,
                    name: tenants.name,
                    emailAddresses: tenants.dokuboxEmailAddresses,
                    key: tenants.dokuboxkey
                })
                .from(tenants)

            const lock = await client.getMailboxLock("INBOX")

            try {

                for await (let msg of client.fetch({ seen: false }, { envelope: true, source: true })) {

                    const parsed = await simpleParser(msg.source)

                    const message = {
                        id: msg.uid,
                        subject: parsed.subject,
                        to: parsed.to?.value || [],
                        cc: parsed.cc?.value || [],
                        attachments: parsed.attachments || []
                    }

                    // -------------------------------------------------
                    // MAPPING / FIND TENANT
                    // -------------------------------------------------
                    const config = await getMessageConfigDrizzle(server, message, tenantList)

                    if (!config) {
                        badMessageDetected = true

                        if (!badMessageMessageSent) {
                            badMessageMessageSent = true
                        }
                        server.log.warn({ messageId: message.id, subject: message.subject }, "Dokubox message could not be mapped to a tenant")
                        continue
                    }

                    if (message.attachments.length > 0) {
                        for (const attachment of message.attachments) {
                            await saveFile(
                                server,
                                config.tenant,
                                message.id,
                                attachment,
                                config.folder,
                                config.filetype
                            )
                        }
                    }
                }

                if (!badMessageDetected) {
                    badMessageDetected = false
                    badMessageMessageSent = false
                }

                await client.messageFlagsAdd({ seen: false }, ["\\Seen"])
                await client.messageDelete({ seen: true })

            } finally {
                lock.release()
                client.close()
            }
        }

    const getMessageConfigDrizzle = async (
        server: FastifyInstance,
        message,
        tenantsList: any[]
    ) => {

        let possibleKeys: string[] = []

        if (message.to) {
            message.to.forEach((item) =>
                possibleKeys.push(item.address.split("@")[0].toLowerCase())
            )
        }

        if (message.cc) {
            message.cc.forEach((item) =>
                possibleKeys.push(item.address.split("@")[0].toLowerCase())
            )
        }

        // -------------------------------------------
        // TENANT IDENTIFY
        // -------------------------------------------
        let tenant = tenantsList.find((t) => possibleKeys.includes(t.key))

        if (!tenant && message.to?.length) {
            const address = message.to[0].address.toLowerCase()

            tenant = tenantsList.find((t) =>
                (t.emailAddresses || []).map((m) => m.toLowerCase()).includes(address)
            )
        }

        if (!tenant) return null

        // -------------------------------------------
        // FOLDER + FILETYPE VIA SUBJECT
        // -------------------------------------------
        let folderId = null
        let filetypeId = null

        // -------------------------------------------
        // Rechnung / Invoice
        // -------------------------------------------
        if (message.subject?.match(/(Rechnung|Beleg|Invoice|Quittung)/gi)) {

            const folder = await server.db
                .select({ id: folders.id })
                .from(folders)
                .where(
                    and(
                        eq(folders.tenant, tenant.id),
                        and(
                            eq(folders.function, "incomingInvoices"),
                            //@ts-ignore
                            eq(folders.year, dayjs().format("YYYY"))
                        )
                    )
                )
                .limit(1)

            folderId = folder[0]?.id ?? null

            const tag = await server.db
                .select({ id: filetags.id })
                .from(filetags)
                .where(
                    and(
                        eq(filetags.tenant, tenant.id),
                        eq(filetags.incomingDocumentType, "invoices")
                    )
                )
                .limit(1)

            filetypeId = tag[0]?.id ?? null
        }

            // -------------------------------------------
            // Mahnung
        // -------------------------------------------
        else if (message.subject?.match(/(Mahnung|Zahlungsaufforderung|Zahlungsverzug)/gi)) {

            const tag = await server.db
                .select({ id: filetags.id })
                .from(filetags)
                .where(
                    and(
                        eq(filetags.tenant, tenant.id),
                        eq(filetags.incomingDocumentType, "reminders")
                    )
                )
                .limit(1)

            filetypeId = tag[0]?.id ?? null
        }

            // -------------------------------------------
            // Sonstige Dokumente → Deposit Folder
        // -------------------------------------------
        else {

            const folder = await server.db
                .select({ id: folders.id })
                .from(folders)
                .where(
                    and(
                        eq(folders.tenant, tenant.id),
                        eq(folders.function, "deposit")
                    )
                )
                .limit(1)

            folderId = folder[0]?.id ?? null
        }


        return {
            tenant: tenant.id,
            folder: folderId,
            filetype: filetypeId
        }
    }

    return {
        run: async () => {
            await syncDokubox()
            console.log("Service: Dokubox sync finished")
        }
    }
}
