import { FastifyInstance } from "fastify"
import { and, eq } from "drizzle-orm"
import bcrypt from "bcrypt"

import {
    accounts,
    authProfiles,
    authRoles,
    authRolePermissions,
    authTenantUsers,
    authUserRoles,
    authUsers,
    branches,
    filetags,
    folders,
    productcategories,
    servicecategories,
    taxTypes,
    teams,
    tenants,
    texttemplates,
    units,
} from "../../db/schema"
import { matrixService } from "./matrix.service"

const adminPermissions = [
    "mcp.tokens.write",
    "staff.time.read_all",
    "masterdata.customers.read",
    "masterdata.vendors.read",
    "masterdata.contacts.read",
    "masterdata.products.read",
    "masterdata.services.read",
    "masterdata.cost_centres.read",
    "masterdata.branches.read",
    "masterdata.teams.read",
    "masterdata.vehicles.read",
    "masterdata.inventory_items.read",
    "masterdata.units.read",
    "accounting.outgoing_documents.read",
    "accounting.outgoing_documents.write",
    "accounting.accounts.read",
    "accounting.incoming_invoices.read",
    "accounting.incoming_invoices.write",
    "accounting.bank.read",
    "accounting.statement_allocations.read",
    "organisation.customers.read",
    "organisation.projects.read",
    "organisation.plants.read",
    "organisation.events.read",
    "organisation.tasks.read",
    "organisation.tasks.write",
]

const defaultUnits = [
    { name: "Stück", single: "Stück", multiple: "Stück", short: "Stk.", step: "1" },
    { name: "Stunde", single: "Stunde", multiple: "Stunden", short: "Std.", step: "0.25" },
    { name: "Pauschale", single: "Pauschale", multiple: "Pauschalen", short: "Psch.", step: "1" },
    { name: "Meter", single: "Meter", multiple: "Meter", short: "m", step: "0.1" },
]

const defaultTaxTypes = [
    { label: "Umsatzsteuer 19%", percentage: 19 },
    { label: "Umsatzsteuer 7%", percentage: 7 },
    { label: "Steuerfrei", percentage: 0 },
]

const defaultAccounts = [
    { number: "8400", label: "Erlöse 19% USt", accountChart: "skr03" },
    { number: "8300", label: "Erlöse 7% USt", accountChart: "skr03" },
    { number: "1200", label: "Bank", accountChart: "skr03" },
    { number: "1000", label: "Kasse", accountChart: "skr03" },
    { number: "1400", label: "Forderungen aus Lieferungen und Leistungen", accountChart: "skr03" },
    { number: "1600", label: "Verbindlichkeiten aus Lieferungen und Leistungen", accountChart: "skr03" },
]

async function ensureGlobalDefaults(server: FastifyInstance, userId: string) {
    for (const unit of defaultUnits) {
        const existing = await server.db.select({ id: units.id }).from(units).where(eq(units.name, unit.name)).limit(1)
        if (!existing.length) await server.db.insert(units).values(unit)
    }

    for (const taxType of defaultTaxTypes) {
        const existing = await server.db
            .select({ id: taxTypes.id })
            .from(taxTypes)
            .where(eq(taxTypes.percentage, taxType.percentage))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(taxTypes).values({
                ...taxType,
                updatedAt: new Date(),
                updatedBy: userId,
            })
        }
    }

    for (const account of defaultAccounts) {
        const existing = await server.db
            .select({ id: accounts.id })
            .from(accounts)
            .where(and(eq(accounts.accountChart, account.accountChart), eq(accounts.number, account.number)))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(accounts).values({
                ...account,
                description: "FEDEO Standardkonto",
            })
        }
    }
}

async function ensureTenantFileDefaults(server: FastifyInstance, tenantId: number, userId: string) {
    const currentYear = new Date().getFullYear()
    const timestamp = new Date()

    const tagDefaults = [
        { name: "Rechnungen", color: "#16a34a", createdDocumentType: "invoices", isSystemUsed: true },
        { name: "Angebote", color: "#2563eb", createdDocumentType: "quotes", isSystemUsed: true },
        { name: "Auftragsbestätigungen", color: "#7c3aed", createdDocumentType: "confirmationOrders", isSystemUsed: true },
        { name: "Lieferscheine", color: "#ea580c", createdDocumentType: "deliveryNotes", isSystemUsed: true },
        { name: "Eingangsrechnungen", color: "#dc2626", incomingDocumentType: "invoices", isSystemUsed: true },
        { name: "Mahnungen", color: "#b91c1c", incomingDocumentType: "reminders", isSystemUsed: true },
    ]

    for (const tag of tagDefaults) {
        const existing = await server.db
            .select({ id: filetags.id })
            .from(filetags)
            .where(and(eq(filetags.tenant, tenantId), eq(filetags.name, tag.name)))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(filetags).values({
                tenant: tenantId,
                ...tag,
            })
        }
    }

    const allTags = await server.db.select().from(filetags).where(eq(filetags.tenant, tenantId))
    const tagByCreatedType = new Map(allTags.map((tag) => [tag.createdDocumentType, tag.id]))
    const tagByIncomingType = new Map(allTags.map((tag) => [tag.incomingDocumentType, tag.id]))

    const rootFolders = [
        { name: "Ausgangsrechnungen", function: "yearSubCategory" as const, icon: "i-heroicons-document-text" },
        { name: "Angebote", function: "yearSubCategory" as const, icon: "i-heroicons-document-duplicate" },
        { name: "Auftragsbestätigungen", function: "yearSubCategory" as const, icon: "i-heroicons-clipboard-document-check" },
        { name: "Lieferscheine", function: "yearSubCategory" as const, icon: "i-heroicons-truck" },
        { name: "Eingangsrechnungen", function: "yearSubCategory" as const, icon: "i-heroicons-inbox-arrow-down" },
        { name: "Belege Bankeinzahlung", function: "yearSubCategory" as const, icon: "i-heroicons-banknotes" },
    ]

    for (const folder of rootFolders) {
        const existing = await server.db
            .select({ id: folders.id })
            .from(folders)
            .where(and(eq(folders.tenant, tenantId), eq(folders.name, folder.name)))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(folders).values({
                tenant: tenantId,
                name: folder.name,
                function: folder.function,
                icon: folder.icon,
                isSystemUsed: true,
                updatedAt: timestamp,
                updatedBy: userId,
            })
        }
    }

    const allFolders = await server.db.select().from(folders).where(eq(folders.tenant, tenantId))
    const rootFolderByName = new Map(allFolders.filter((folder) => !folder.parent).map((folder) => [folder.name, folder.id]))

    const yearFolders = [
        {
            parentName: "Ausgangsrechnungen",
            function: "invoices" as const,
            icon: "i-heroicons-document-text",
            standardFiletype: tagByCreatedType.get("invoices"),
        },
        {
            parentName: "Angebote",
            function: "quotes" as const,
            icon: "i-heroicons-document-duplicate",
            standardFiletype: tagByCreatedType.get("quotes"),
        },
        {
            parentName: "Auftragsbestätigungen",
            function: "confirmationOrders" as const,
            icon: "i-heroicons-clipboard-document-check",
            standardFiletype: tagByCreatedType.get("confirmationOrders"),
        },
        {
            parentName: "Lieferscheine",
            function: "deliveryNotes" as const,
            icon: "i-heroicons-truck",
            standardFiletype: tagByCreatedType.get("deliveryNotes"),
        },
        {
            parentName: "Eingangsrechnungen",
            function: "incomingInvoices" as const,
            icon: "i-heroicons-inbox-arrow-down",
            standardFiletype: tagByIncomingType.get("invoices"),
        },
        {
            parentName: "Belege Bankeinzahlung",
            function: "deposit" as const,
            icon: "i-heroicons-banknotes",
        },
    ]

    for (const folder of yearFolders) {
        const parent = rootFolderByName.get(folder.parentName)
        if (!parent) continue

        const existing = await server.db
            .select({ id: folders.id })
            .from(folders)
            .where(and(eq(folders.tenant, tenantId), eq(folders.parent, parent), eq(folders.year, currentYear)))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(folders).values({
                tenant: tenantId,
                name: String(currentYear),
                parent,
                function: folder.function,
                year: currentYear,
                icon: folder.icon,
                standardFiletype: folder.standardFiletype,
                standardFiletypeIsOptional: false,
                isSystemUsed: true,
                updatedAt: timestamp,
                updatedBy: userId,
            })
        }
    }
}

export async function ensureTenantBaseData(server: FastifyInstance, tenantId: number, adminUserId: string) {
    await ensureGlobalDefaults(server, adminUserId)
    await ensureTenantFileDefaults(server, tenantId, adminUserId)

    const [adminRole] = await server.db
        .select({ id: authRoles.id })
        .from(authRoles)
        .where(and(eq(authRoles.tenant_id, tenantId), eq(authRoles.name, "Administrator")))
        .limit(1)

    let adminRoleId = adminRole?.id
    if (!adminRoleId) {
        const [createdRole] = await server.db
            .insert(authRoles)
            .values({
                name: "Administrator",
                description: "Vollzugriff für die Administration dieses Mandanten",
                tenant_id: tenantId,
                created_by: adminUserId,
            })
            .returning({ id: authRoles.id })

        adminRoleId = createdRole.id
    }

    for (const permission of adminPermissions) {
        const existing = await server.db
            .select()
            .from(authRolePermissions)
            .where(and(eq(authRolePermissions.role_id, adminRoleId), eq(authRolePermissions.permission, permission)))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(authRolePermissions).values({
                role_id: adminRoleId,
                permission,
            })
        }
    }

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

    if (!membership.length) {
        await server.db.insert(authTenantUsers).values({
            tenant_id: tenantId,
            user_id: adminUserId,
            created_by: adminUserId,
        })
    }

    const roleAssignment = await server.db
        .select()
        .from(authUserRoles)
        .where(and(
            eq(authUserRoles.tenant_id, tenantId),
            eq(authUserRoles.user_id, adminUserId),
            eq(authUserRoles.role_id, adminRoleId),
        ))
        .limit(1)

    if (!roleAssignment.length) {
        await server.db.insert(authUserRoles).values({
            tenant_id: tenantId,
            user_id: adminUserId,
            role_id: adminRoleId,
            created_by: adminUserId,
        })
    }

    const profile = await server.db
        .select()
        .from(authProfiles)
        .where(and(eq(authProfiles.tenant_id, tenantId), eq(authProfiles.user_id, adminUserId)))
        .limit(1)

    if (!profile.length) {
        await server.db.insert(authProfiles).values({
            tenant_id: tenantId,
            user_id: adminUserId,
            first_name: process.env.FEDEO_BOOTSTRAP_ADMIN_FIRST_NAME || "Admin",
            last_name: process.env.FEDEO_BOOTSTRAP_ADMIN_LAST_NAME || "Benutzer",
            email: process.env.FEDEO_BOOTSTRAP_ADMIN_EMAIL?.trim().toLowerCase(),
            active: true,
        })
    }

    const branch = await server.db
        .select({ id: branches.id })
        .from(branches)
        .where(and(eq(branches.tenant, tenantId), eq(branches.name, "Hauptstandort")))
        .limit(1)

    let branchId = branch[0]?.id
    if (!branchId) {
        const [createdBranch] = await server.db.insert(branches).values({
            tenant: tenantId,
            name: "Hauptstandort",
            number: "001",
            description: "Standardstandort",
            updatedAt: new Date(),
            updatedBy: adminUserId,
        }).returning({ id: branches.id })
        branchId = createdBranch.id
    }

    const team = await server.db
        .select({ id: teams.id })
        .from(teams)
        .where(and(eq(teams.tenant, tenantId), eq(teams.name, "Standardteam")))
        .limit(1)

    if (!team.length) {
        await server.db.insert(teams).values({
            tenant: tenantId,
            name: "Standardteam",
            description: "Automatisch angelegtes Standardteam",
            branch: branchId,
            updatedAt: new Date(),
            updatedBy: adminUserId,
        })
    }

    const defaultProductCategory = await server.db
        .select({ id: productcategories.id })
        .from(productcategories)
        .where(and(eq(productcategories.tenant, tenantId), eq(productcategories.name, "Standard")))
        .limit(1)

    if (!defaultProductCategory.length) {
        await server.db.insert(productcategories).values({
            tenant: tenantId,
            name: "Standard",
            description: "Standardkategorie",
            updatedAt: new Date(),
            updatedBy: adminUserId,
        })
    }

    const defaultServiceCategory = await server.db
        .select({ id: servicecategories.id })
        .from(servicecategories)
        .where(and(eq(servicecategories.tenant, tenantId), eq(servicecategories.name, "Standard")))
        .limit(1)

    if (!defaultServiceCategory.length) {
        await server.db.insert(servicecategories).values({
            tenant: tenantId,
            name: "Standard",
            description: "Standardkategorie",
            updated_at: new Date(),
            updated_by: adminUserId,
        })
    }

    const templateDefaults = [
        { name: "Standard Einleitung", pos: "startText" as const, text: "<p>vielen Dank für Ihre Anfrage.</p>" },
        { name: "Standard Schluss", pos: "endText" as const, text: "<p>Mit freundlichen Grüßen</p>" },
    ]

    for (const template of templateDefaults) {
        const existing = await server.db
            .select({ id: texttemplates.id })
            .from(texttemplates)
            .where(and(eq(texttemplates.tenant, tenantId), eq(texttemplates.name, template.name)))
            .limit(1)

        if (!existing.length) {
            await server.db.insert(texttemplates).values({
                tenant: tenantId,
                name: template.name,
                text: template.text,
                pos: template.pos,
                default: true,
                updatedAt: new Date(),
                updatedBy: adminUserId,
            })
        }
    }
}

export async function runBootstrap(server: FastifyInstance) {
    const email = process.env.FEDEO_BOOTSTRAP_ADMIN_EMAIL?.trim().toLowerCase()
    const password = process.env.FEDEO_BOOTSTRAP_ADMIN_PASSWORD

    if (!email && !password) return
    if (!email || !password) {
        throw new Error("FEDEO_BOOTSTRAP_ADMIN_EMAIL und FEDEO_BOOTSTRAP_ADMIN_PASSWORD müssen gemeinsam gesetzt sein")
    }

    const [existingUser] = await server.db
        .select()
        .from(authUsers)
        .where(eq(authUsers.email, email))
        .limit(1)

    let adminUser = existingUser
    if (!adminUser) {
        const [createdUser] = await server.db.insert(authUsers).values({
            email,
            passwordHash: await bcrypt.hash(password, 10),
            is_admin: true,
            multiTenant: true,
            must_change_password: false,
            updatedAt: new Date(),
        }).returning()

        adminUser = createdUser
        console.log(`✅ Bootstrap-Admin angelegt: ${email}`)
    } else if (!adminUser.is_admin) {
        const [updatedUser] = await server.db.update(authUsers).set({
            is_admin: true,
            updatedAt: new Date(),
        }).where(eq(authUsers.id, adminUser.id)).returning()

        adminUser = updatedUser
        console.log(`✅ Bootstrap-Adminrechte gesetzt: ${email}`)
    }

    const tenantName = process.env.FEDEO_BOOTSTRAP_TENANT_NAME?.trim() || "FEDEO"
    const tenantShort = process.env.FEDEO_BOOTSTRAP_TENANT_SHORT?.trim() || "FEDEO"

    const [existingTenant] = await server.db
        .select()
        .from(tenants)
        .where(eq(tenants.short, tenantShort))
        .limit(1)

    let tenant = existingTenant
    if (!tenant) {
        const [createdTenant] = await server.db.insert(tenants).values({
            name: tenantName,
            short: tenantShort,
            updatedAt: new Date(),
            updatedBy: adminUser.id,
        }).returning()

        tenant = createdTenant
        console.log(`✅ Bootstrap-Mandant angelegt: ${tenant.name}`)
    }

    await ensureTenantBaseData(server, tenant.id, adminUser.id)
    console.log("✅ Bootstrap-Grunddaten geprüft")

    if (process.env.FEDEO_BOOTSTRAP_MATRIX === "true") {
        try {
            const matrix = matrixService(server)
            await matrix.provisionTenantRoom(adminUser.id, tenant.id, {
                key: "allgemein",
                name: "Allgemeiner Chat",
                type: "general",
            })
            console.log("✅ Bootstrap-Matrix-Kommunikation geprüft")
        } catch (err) {
            console.error("❌ Bootstrap-Matrix-Kommunikation fehlgeschlagen:", err)
            throw err
        }
    }
}
