import { FastifyInstance } from "fastify"
import dayjs from "dayjs"
import { getInvoiceDataFromGPT } from "../../utils/gpt"
import { loadFileBuffer } from "../../utils/fileBuffer"
import { detectElectronicInvoice } from "../einvoice/detectElectronicInvoice"
import { parseElectronicInvoice } from "../einvoice/parseElectronicInvoice"
import {
    findElectronicInvoiceVendor,
    mapElectronicInvoiceToIncomingInvoice,
} from "../einvoice/mapElectronicInvoice"

// Drizzle schema
import {
    tenants,
    files,
    filetags,
    incominginvoices,
} from "../../../db/schema"

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

const formatInvoiceItemDescription = (item: any) => {
    const parts = [
        typeof item.description === "string" ? item.description.trim() : "",
        item.quantity !== null && item.quantity !== undefined
            ? [item.quantity, item.unit].filter(Boolean).join(" ")
            : (typeof item.unit === "string" ? item.unit.trim() : ""),
        typeof item.total === "number" ? `${item.total.toFixed(2)} EUR` : "",
    ].filter(Boolean)

    return parts.join(" - ")
}

const mapGptDataToIncomingInvoice = (data: any, tenantId: number) => {
    const itemInfo: any = {
        tenant: tenantId,
        state: "Vorbereitet",
        preparationSource: "gpt",
    }

    if (data.invoice_number) itemInfo.reference = data.invoice_number
    if (data.invoice_date && dayjs(data.invoice_date).isValid()) itemInfo.date = dayjs(data.invoice_date).toISOString()
    if (data.issuer?.id) itemInfo.vendor = data.issuer.id
    if (data.invoice_duedate && dayjs(data.invoice_duedate).isValid()) itemInfo.dueDate = dayjs(data.invoice_duedate).toISOString()

    const mapPayment: Record<string, string> = {
        "Direct Debit": "Einzug",
        "Transfer": "Überweisung",
        "Credit Card": "Kreditkarte",
        "Other": "Sonstiges",
    }
    if (data.terms) itemInfo.paymentType = mapPayment[data.terms] ?? data.terms

    if (data.invoice_items?.length > 0) {
        itemInfo.accounts = data.invoice_items
            .filter((item: any) => item.description || item.total !== null || item.total_without_tax !== null)
            .map((item: any) => {
                const total = typeof item.total === "number" ? item.total : null
                const totalWithoutTax = typeof item.total_without_tax === "number" ? item.total_without_tax : null
                const amountTax = total !== null && totalWithoutTax !== null
                    ? Number((total - totalWithoutTax).toFixed(2))
                    : null

                return {
                    account: item.account_id,
                    description: item.description,
                    amountNet: totalWithoutTax,
                    amountTax,
                    taxType: item.tax_rate !== null ? String(item.tax_rate) : null,
                    amountGross: total,
                    costCentre: null,
                    quantity: item.quantity,
                }
            })
    }

    let description = ""
    if (data.delivery_note_number) description += `Lieferschein: ${data.delivery_note_number}\n`
    if (data.reference) description += `Referenz: ${data.reference}\n`
    for (const item of data.invoice_items || []) {
        const line = formatInvoiceItemDescription(item)
        if (line) description += `${line}\n`
    }
    itemInfo.description = description.trim()

    return itemInfo
}

export function prepareIncomingInvoices(server: FastifyInstance) {
    const processInvoices = async (tenantId:number) => {
        console.log("▶ Starting Incoming Invoice Preparation")

        const tenantsRes = await server.db
            .select()
            .from(tenants)
            .where(eq(tenants.id, tenantId))
            .orderBy(tenants.id)

        if (!tenantsRes.length) {
            console.log("No tenants with autoPrepareIncomingInvoices = true")
            return
        }

        console.log(`Processing tenants: ${tenantsRes.map(t => t.id).join(", ")}`)

        // -------------------------------------------------------------
        // 2️⃣ Jeden Tenant einzeln verarbeiten
        // -------------------------------------------------------------
        for (const tenant of tenantsRes) {
            const tenantId = tenant.id

            // 2.1 Datei-Tags holen für incoming invoices
            const tagRes = await server.db
                .select()
                .from(filetags)
                .where(
                    and(
                        eq(filetags.tenant, tenantId),
                        eq(filetags.incomingDocumentType, "invoices")
                    )
                )
                .limit(1)

            const invoiceFileTag = tagRes?.[0]?.id
            if (!invoiceFileTag) {
                server.log.error(`❌ Missing filetag 'invoices' for tenant ${tenantId}`)
                continue
            }

            // 2.2 Alle Dateien laden, die als Invoice markiert sind aber NOCH keine incominginvoice haben
            const filesRes = await server.db
                .select()
                .from(files)
                .where(
                    and(
                        eq(files.tenant, tenantId),
                        eq(files.type, invoiceFileTag),
                        isNull(files.incominginvoice),
                        eq(files.archived, false),
                        not(isNull(files.path))
                    )
                )

            if (!filesRes.length) {
                console.log(`No invoice files for tenant ${tenantId}`)
                continue
            }

            // -------------------------------------------------------------
            // 3️⃣ Strukturierte E-Rechnung bevorzugen, GPT als PDF-Fallback verwenden
            // -------------------------------------------------------------
            for (const file of filesRes) {
                console.log(`Processing file ${file.id} for tenant ${tenantId}`)

                let fileData: Buffer

                try {
                    fileData = await loadFileBuffer(file.path!)
                } catch (error) {
                    server.log.error(error, `Datei ${file.id} konnte nicht aus S3 geladen werden.`)
                    continue
                }

                const electronicInvoice = await detectElectronicInvoice(fileData, file)

                let itemInfo: any = null

                if (electronicInvoice) {
                    server.log.info({
                        fileId: file.id,
                        container: electronicInvoice.container,
                        syntax: electronicInvoice.syntax,
                        attachmentName: electronicInvoice.attachmentName,
                    }, "Strukturierte E-Rechnung erkannt.")

                    try {
                        const parsedInvoice = parseElectronicInvoice(electronicInvoice.xml, electronicInvoice.syntax)
                        const vendor = await findElectronicInvoiceVendor(server, tenantId, parsedInvoice)
                        itemInfo = mapElectronicInvoiceToIncomingInvoice(
                            parsedInvoice,
                            tenantId,
                            vendor?.id || null,
                            electronicInvoice.container,
                            electronicInvoice.attachmentName,
                        )
                    } catch (error) {
                        server.log.error(error, `E-Rechnung aus Datei ${file.id} konnte nicht verarbeitet werden.`)

                        if (electronicInvoice.container === "xml") continue
                    }
                }

                if (!itemInfo) {
                    const data = await getInvoiceDataFromGPT(server, file, tenantId, fileData)

                    if (!data) {
                        server.log.warn(`GPT returned no data for file ${file.id}`)
                        continue
                    }

                    itemInfo = mapGptDataToIncomingInvoice(data, tenantId)
                }

                // ---------------------------------------------------------
                // 4️⃣ IncomingInvoice erstellen
                // ---------------------------------------------------------
                const inserted = await server.db
                    .insert(incominginvoices)
                    .values(itemInfo)
                    .returning()

                const newInvoice = inserted?.[0]

                if (!newInvoice) {
                    server.log.error(`Failed to insert incoming invoice for file ${file.id}`)
                    continue
                }

                // ---------------------------------------------------------
                // 5️⃣ Datei mit incominginvoice-ID verbinden
                // ---------------------------------------------------------
                await server.db
                    .update(files)
                    .set({ incominginvoice: newInvoice.id })
                    .where(eq(files.id, file.id))

                console.log(`IncomingInvoice ${newInvoice.id} created for file ${file.id}`)
            }

        }


        return
    }

    return {
        run: async (tenant:number) => {
            await processInvoices(tenant)
            console.log("Incoming Invoice Preparation Completed.")

        }
    }

}
