import { FastifyInstance } from "fastify"
import multipart from "@fastify/multipart"
import { s3 } from "../utils/s3"
import {
    GetObjectCommand
} from "@aws-sdk/client-s3"
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"
import archiver from "archiver"
import { secrets } from "../utils/secrets"
import { saveFile } from "../utils/files"

import { eq, inArray } from "drizzle-orm"
import { and } from "drizzle-orm"
import {
    authProfiles,
    files,
    createddocuments,
    customers
} from "../../db/schema"


export default async function fileRoutes(server: FastifyInstance) {
    const getPortalCustomerId = async (req: any) => {
        const tenantId = req.user?.tenant_id
        const userId = req.user?.user_id

        if (!tenantId || !userId) return null

        const [profile] = await server.db
            .select({ customer_for_portal: authProfiles.customer_for_portal })
            .from(authProfiles)
            .where(and(
                eq(authProfiles.tenant_id, tenantId),
                eq(authProfiles.user_id, userId)
            ))
            .limit(1)

        return profile?.customer_for_portal || null
    }

    const loadSingleFileForRequest = async (req: any, id: string) => {
        const tenantId = req.user?.tenant_id
        if (!tenantId) return null

        const portalCustomerId = await getPortalCustomerId(req)

        if (!portalCustomerId) {
            const rows = await server.db
                .select()
                .from(files)
                .where(and(eq(files.id, id), eq(files.tenant, tenantId)))

            return rows[0] || null
        }

        const rows = await server.db
            .select({
                file: files,
            })
            .from(files)
            .leftJoin(createddocuments, eq(files.createddocument, createddocuments.id))
            .where(and(
                eq(files.id, id),
                eq(files.tenant, tenantId),
                eq(createddocuments.customer, portalCustomerId),
                eq(createddocuments.availableInPortal, true)
            ))
            .limit(1)

        return rows[0]?.file || null
    }

    // -------------------------------------------------------------
    // MULTIPART INIT
    // -------------------------------------------------------------
    await server.register(multipart, {
        limits: { fileSize: 20 * 1024 * 1024 } // 20 MB
    })


    // -------------------------------------------------------------
    // UPLOAD FILE
    // -------------------------------------------------------------
    server.post("/files/upload", async (req, reply) => {
        try {
            const tenantId = req.user?.tenant_id
            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            const data: any = await req.file()
            if (!data?.file) return reply.code(400).send({ error: "No file uploaded" })
            const fileBuffer = await data.toBuffer()

            const meta = data.fields?.meta?.value ? JSON.parse(data.fields.meta.value) : {}
            const { folder = null, type = null, ...otherMeta } = meta

            const created = await saveFile(
                server,
                tenantId,
                null,
                {
                    filename: data.filename,
                    content: fileBuffer,
                    contentType: data.mimetype
                },
                folder,
                type,
                otherMeta
            )

            if (!created) throw new Error("Could not create DB entry")

            return {
                id: created.id,
                filename: created.filename,
                path: created.key
            }
        } catch (err) {
            console.error(err)
            return reply.code(500).send({ error: "Upload failed" })
        }
    })



    // -------------------------------------------------------------
    // GET FILE OR LIST FILES
    // -------------------------------------------------------------
    server.get("/files/:id?", async (req, reply) => {
        try {
            const { id } = req.params as { id?: string }

            // 🔹 EINZELNE DATEI
            if (id) {
                const file = await loadSingleFileForRequest(req, id)
                if (!file) return reply.code(404).send({ error: "Not found" })

                return file
            }

            // 🔹 ALLE DATEIEN DES TENANTS (mit createddocument + customer)
            const tenantId = req.user?.tenant_id
            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            const list = await server.db
                //@ts-ignore
                .select({
                    ...files,
                    createddocument: createddocuments,
                    customer: customers
                })
                .from(files)
                .leftJoin(
                    createddocuments,
                    eq(files.createddocument, createddocuments.id)
                )
                .leftJoin(
                    customers,
                    eq(createddocuments.customer, customers.id)
                )
                .where(eq(files.tenant, tenantId))

            return { files: list }
        } catch (err) {
            console.error(err)
            return reply.code(500).send({ error: "Could not load files" })
        }
    })



    // -------------------------------------------------------------
    // DOWNLOAD (SINGLE OR MULTI ZIP)
    // -------------------------------------------------------------
    server.post("/files/download/:id?", async (req, reply) => {
        try {
            const { id } = req.params as { id?: string }
            //@ts-ignore
            const ids = req.body?.ids || []

            // -------------------------------------------------
            // 1️⃣ SINGLE DOWNLOAD
            // -------------------------------------------------
            if (id) {
                const file = await loadSingleFileForRequest(req, id)
                if (!file) return reply.code(404).send({ error: "File not found" })

                const command = new GetObjectCommand({
                    Bucket: secrets.S3_BUCKET,
                    Key: file.path!
                })

                const { Body, ContentType } = await s3.send(command)

                const chunks: any[] = []
                for await (const chunk of Body as any) chunks.push(chunk)
                const buffer = Buffer.concat(chunks)

                reply.header("Content-Type", ContentType || "application/octet-stream")
                reply.header("Content-Disposition", `attachment; filename="${file.path?.split("/").pop()}"`)
                return reply.send(buffer)
            }


            // -------------------------------------------------
            // 2️⃣ MULTI DOWNLOAD → ZIP
            // -------------------------------------------------
            if (Array.isArray(ids) && ids.length > 0) {
                const rows = await server.db
                    .select()
                    .from(files)
                    .where(inArray(files.id, ids))

                if (!rows.length) return reply.code(404).send({ error: "Files not found" })

                reply.header("Content-Type", "application/zip")
                reply.header("Content-Disposition", `attachment; filename="dateien.zip"`)

                const archive = archiver("zip", { zlib: { level: 9 } })

                for (const entry of rows) {
                    const cmd = new GetObjectCommand({
                        Bucket: secrets.S3_BUCKET,
                        Key: entry.path!
                    })
                    const { Body } = await s3.send(cmd)

                    archive.append(Body as any, {
                        name: entry.path?.split("/").pop() || entry.id
                    })
                }

                await archive.finalize()
                return reply.send(archive)
            }

            return reply.code(400).send({ error: "No id or ids provided" })

        } catch (err) {
            console.error(err)
            return reply.code(500).send({ error: "Download failed" })
        }
    })



    // -------------------------------------------------------------
    // GENERATE PRESIGNED URL(S)
    // -------------------------------------------------------------
    server.post("/files/presigned/:id?", async (req, reply) => {
        try {
            const { id } = req.params as { id?: string }
            const { ids } = req.body as { ids?: string[] }
            const tenantId = req.user?.tenant_id

            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            // -------------------------------------------------
            // SINGLE FILE PRESIGNED URL
            // -------------------------------------------------
            if (id) {
                const file = await loadSingleFileForRequest(req, id)
                if (!file) return reply.code(404).send({ error: "Not found" })

                const url = await getSignedUrl(
                    s3,
                    new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: file.path! }),
                    { expiresIn: 900 }
                )

                return { ...file, url }
            } else {
                // -------------------------------------------------
                // MULTIPLE PRESIGNED URLs
                // -------------------------------------------------
                if (!ids || !Array.isArray(ids) || ids.length === 0) {
                    return { files: [] }
                }

                const rows = await server.db
                    .select()
                    .from(files)
                    .where(eq(files.tenant, tenantId))

                const selected = rows.filter(f => ids.includes(f.id) && f.path)

                console.log(selected)

                const url = await getSignedUrl(
                    s3,
                    new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: selected[0].path! }),
                    { expiresIn: 900 }
                )
                console.log(url)
                console.log(selected.filter(f => !f.path))

                const output = await Promise.all(
                    selected.map(async (file) => {
                        const url = await getSignedUrl(
                            s3,
                            new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: file.path! }),
                            { expiresIn: 900 }
                        )
                        return { ...file, url }
                    })
                )

                return { files: output }
            }



        } catch (err) {
            console.error(err)
            return reply.code(500).send({ error: "Could not create presigned URLs" })
        }
    })

}
