import { FastifyInstance } from "fastify"
import jwt from "jsonwebtoken"
import { secrets } from "../utils/secrets"
import { createHash, randomBytes } from "node:crypto"

import {
    authTenantUsers,
    authUsers,
    authProfiles,
    tenants,
    m2mApiKeys
} from "../../db/schema"

import {and, desc, eq, inArray} from "drizzle-orm"
import { enrichProfilesWithBranches } from "../utils/profileBranches"
import { enrichProfilesWithTeams } from "../utils/profileTeams"
import { rotateRefreshToken } from "../utils/authTokens"


export default async function tenantRoutes(server: FastifyInstance) {
    const generateApiKey = () => {
        const raw = randomBytes(32).toString("base64url")
        return `fedeo_m2m_${raw}`
    }
    const hashApiKey = (apiKey: string) =>
        createHash("sha256").update(apiKey, "utf8").digest("hex")


    // -------------------------------------------------------------
    // GET CURRENT TENANT
    // -------------------------------------------------------------
    server.get("/tenant", async (req, reply) => {
        if (req.user?.tenant_id) {
            const tenantRows = await server.db
                .select()
                .from(tenants)
                .where(eq(tenants.id, Number(req.user.tenant_id)))
                .limit(1)

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

            return tenantRows[0]
        }

        if (req.tenant) {
            return {
                message: `Hallo vom Tenant ${req.tenant?.name}`,
                tenant_id: req.tenant?.id,
            }
        }
        return {
            message: "Server ist im MultiTenant-Modus – es werden alle verfügbaren Tenants geladen."
        }
    })



    // -------------------------------------------------------------
    // SWITCH TENANT
    // -------------------------------------------------------------
    server.post("/tenant/switch", async (req, reply) => {
        try {
            if (!req.user) {
                return reply.code(401).send({ error: "Unauthorized" })
            }

            const { tenant_id, refreshToken } = req.body as { tenant_id: string; refreshToken?: string }
            if (!tenant_id) return reply.code(400).send({ error: "tenant_id required" })

            // prüfen ob der User zu diesem Tenant gehört
            const membership = await server.db
                .select()
                .from(authTenantUsers)
                .where(and(
                        eq(authTenantUsers.user_id, req.user.user_id),
                        eq(authTenantUsers.tenant_id, Number(tenant_id))
                ))

            if (!membership.length) {
                return reply.code(403).send({ error: "Not a member of this tenant" })
            }

            if (refreshToken) {
                const session = await rotateRefreshToken(
                    server,
                    refreshToken,
                    Number(tenant_id),
                    req.user.user_id
                )

                if (!session) {
                    return reply.code(401).send({ error: "Invalid or expired refresh token" })
                }

                return session
            }

            // Abwärtskompatibilität für Clients ohne persistente Session
            const token = jwt.sign(
                {
                    user_id: req.user.user_id,
                    email: req.user.email,
                    tenant_id,
                },
                secrets.JWT_SECRET!,
                { expiresIn: "6h" }
            )

            reply.setCookie("token", token, {
                path: "/",
                httpOnly: true,
                sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
                secure: process.env.NODE_ENV === "production",
                maxAge: 60 * 60 * 6,
            })

            return { token }

        } catch (err) {
            console.error("TENANT SWITCH ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })



    // -------------------------------------------------------------
    // TENANT USERS (auth_users + auth_profiles)
    // -------------------------------------------------------------
    server.get("/tenant/users", async (req, reply) => {
        try {
            const authUser = req.user
            if (!authUser) return reply.code(401).send({ error: "Unauthorized" })

            const tenantId = authUser.tenant_id

            // 1) auth_tenant_users → user_ids
            const tenantUsers = await server.db
                .select()
                .from(authTenantUsers)
                .where(eq(authTenantUsers.tenant_id, tenantId))

            const userIds = tenantUsers.map(u => u.user_id)

            if (!userIds.length) {
                return { tenant_id: tenantId, users: [] }
            }

            // 2) auth_users laden
            const users = await server.db
                .select()
                .from(authUsers)
                .where(inArray(authUsers.id, userIds))

            // 3) auth_profiles pro Tenant laden
            const profileRows = await server.db
                .select()
                .from(authProfiles)
                .where(
                    and(
                        eq(authProfiles.tenant_id, tenantId),
                        inArray(authProfiles.user_id, userIds)
                    ))
            const profilesWithBranches = await enrichProfilesWithBranches(server, profileRows)
            const profiles = await enrichProfilesWithTeams(server, profilesWithBranches)

            const combined = users.map(u => {
                const profile = profiles.find(p => p.user_id === u.id)
                return {
                    id: u.id,
                    email: u.email,
                    profile,
                    full_name: profile?.full_name ?? null
                }
            })

            return { tenant_id: tenantId, users: combined }

        } catch (err) {
            console.error("/tenant/users ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })



    // -------------------------------------------------------------
    // TENANT PROFILES
    // -------------------------------------------------------------
    server.get("/tenant/profiles", async (req, reply) => {
        try {
            const tenantId = req.user?.tenant_id
            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            const profileRows = await server.db
                .select()
                .from(authProfiles)
                .where(eq(authProfiles.tenant_id, tenantId))

            const profilesWithBranches = await enrichProfilesWithBranches(server, profileRows)
            const data = await enrichProfilesWithTeams(server, profilesWithBranches)
            return { data }

        } catch (err) {
            console.error("/tenant/profiles ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })



    // -------------------------------------------------------------
    // UPDATE NUMBER RANGE
    // -------------------------------------------------------------
    server.put("/tenant/numberrange/:numberrange", async (req, reply) => {
        try {
            const user = req.user
            if (!user) return reply.code(401).send({ error: "Unauthorized" })

            const { numberrange } = req.params as { numberrange: string }
            const { numberRange } = req.body as { numberRange: any }

            if (!numberRange) {
                return reply.code(400).send({ error: "numberRange required" })
            }

            const tenantId = Number(user.tenant_id)

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

            const current = currentTenantRows[0]
            if (!current) return reply.code(404).send({ error: "Tenant not found" })

            const updatedRanges = {
                //@ts-ignore
                ...current.numberRanges,
                [numberrange]: numberRange
            }

            const updated = await server.db
                .update(tenants)
                .set({ numberRanges: updatedRanges })
                .where(eq(tenants.id, tenantId))
                .returning()

            return updated[0]

        } catch (err) {
            console.error("/tenant/numberrange ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })



    // -------------------------------------------------------------
    // UPDATE TENANT OTHER FIELDS
    // -------------------------------------------------------------
    server.put("/tenant/other/:id", async (req, reply) => {
        try {
            const user = req.user
            if (!user) return reply.code(401).send({ error: "Unauthorized" })

            const { id } = req.params as { id: string }
            const { data } = req.body as { data: any }

            if (!data) return reply.code(400).send({ error: "data required" })

            const updated = await server.db
                .update(tenants)
                .set(data)
                .where(eq(tenants.id, Number(user.tenant_id)))
                .returning()

            return updated[0]

        } catch (err) {
            console.error("/tenant/other ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })

    // -------------------------------------------------------------
    // M2M API KEYS
    // -------------------------------------------------------------
    server.get("/tenant/api-keys", async (req, reply) => {
        try {
            const tenantId = req.user?.tenant_id
            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            const keys = await server.db
                .select({
                    id: m2mApiKeys.id,
                    name: m2mApiKeys.name,
                    tenant_id: m2mApiKeys.tenantId,
                    user_id: m2mApiKeys.userId,
                    active: m2mApiKeys.active,
                    key_prefix: m2mApiKeys.keyPrefix,
                    created_at: m2mApiKeys.createdAt,
                    updated_at: m2mApiKeys.updatedAt,
                    expires_at: m2mApiKeys.expiresAt,
                    last_used_at: m2mApiKeys.lastUsedAt,
                })
                .from(m2mApiKeys)
                .where(eq(m2mApiKeys.tenantId, tenantId))
                .orderBy(desc(m2mApiKeys.createdAt))

            return keys
        } catch (err) {
            console.error("/tenant/api-keys GET ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })

    server.post("/tenant/api-keys", async (req, reply) => {
        try {
            const tenantId = req.user?.tenant_id
            const creatorUserId = req.user?.user_id
            if (!tenantId || !creatorUserId) {
                return reply.code(401).send({ error: "Unauthorized" })
            }

            const { name, user_id, expires_at } = req.body as {
                name: string
                user_id: string
                expires_at?: string | null
            }

            if (!name || !user_id) {
                return reply.code(400).send({ error: "name and user_id are required" })
            }

            const userMembership = await server.db
                .select()
                .from(authTenantUsers)
                .where(and(
                    eq(authTenantUsers.tenant_id, tenantId),
                    eq(authTenantUsers.user_id, user_id)
                ))
                .limit(1)

            if (!userMembership[0]) {
                return reply.code(400).send({ error: "user_id is not assigned to this tenant" })
            }

            const plainApiKey = generateApiKey()
            const keyPrefix = plainApiKey.slice(0, 16)
            const keyHash = hashApiKey(plainApiKey)

            const inserted = await server.db
                .insert(m2mApiKeys)
                .values({
                    tenantId,
                    userId: user_id,
                    createdBy: creatorUserId,
                    name,
                    keyPrefix,
                    keyHash,
                    expiresAt: expires_at ? new Date(expires_at) : null,
                })
                .returning({
                    id: m2mApiKeys.id,
                    name: m2mApiKeys.name,
                    tenant_id: m2mApiKeys.tenantId,
                    user_id: m2mApiKeys.userId,
                    key_prefix: m2mApiKeys.keyPrefix,
                    created_at: m2mApiKeys.createdAt,
                    expires_at: m2mApiKeys.expiresAt,
                    active: m2mApiKeys.active,
                })

            return reply.code(201).send({
                ...inserted[0],
                api_key: plainApiKey, // only returned once
            })
        } catch (err) {
            console.error("/tenant/api-keys POST ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })

    server.patch("/tenant/api-keys/:id", async (req, reply) => {
        try {
            const tenantId = req.user?.tenant_id
            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            const { id } = req.params as { id: string }
            const { name, active, expires_at } = req.body as {
                name?: string
                active?: boolean
                expires_at?: string | null
            }

            const updateData: any = {
                updatedAt: new Date()
            }
            if (name !== undefined) updateData.name = name
            if (active !== undefined) updateData.active = active
            if (expires_at !== undefined) updateData.expiresAt = expires_at ? new Date(expires_at) : null

            const updated = await server.db
                .update(m2mApiKeys)
                .set(updateData)
                .where(and(
                    eq(m2mApiKeys.id, id),
                    eq(m2mApiKeys.tenantId, tenantId)
                ))
                .returning({
                    id: m2mApiKeys.id,
                    name: m2mApiKeys.name,
                    tenant_id: m2mApiKeys.tenantId,
                    user_id: m2mApiKeys.userId,
                    active: m2mApiKeys.active,
                    key_prefix: m2mApiKeys.keyPrefix,
                    updated_at: m2mApiKeys.updatedAt,
                    expires_at: m2mApiKeys.expiresAt,
                    last_used_at: m2mApiKeys.lastUsedAt,
                })

            if (!updated[0]) {
                return reply.code(404).send({ error: "API key not found" })
            }

            return updated[0]
        } catch (err) {
            console.error("/tenant/api-keys PATCH ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })

    server.delete("/tenant/api-keys/:id", async (req, reply) => {
        try {
            const tenantId = req.user?.tenant_id
            if (!tenantId) return reply.code(401).send({ error: "Unauthorized" })

            const { id } = req.params as { id: string }
            await server.db
                .delete(m2mApiKeys)
                .where(and(
                    eq(m2mApiKeys.id, id),
                    eq(m2mApiKeys.tenantId, tenantId)
                ))

            return { success: true }
        } catch (err) {
            console.error("/tenant/api-keys DELETE ERROR:", err)
            return reply.code(500).send({ error: "Internal Server Error" })
        }
    })

}
