import { and, desc, eq, ilike, or } from "drizzle-orm"
import { customers, events, plants, projects, tasks } from "../../../db/schema"
import { McpTool } from "../types"

const limitFromArgs = (args: Record<string, unknown>, fallback = 25) => {
    const raw = Number(args.limit ?? fallback)
    if (!Number.isFinite(raw)) return fallback
    return Math.min(Math.max(Math.trunc(raw), 1), 100)
}

const stringArg = (args: Record<string, unknown>, key: string) => {
    const value = args[key]
    return typeof value === "string" && value.trim() ? value.trim() : null
}

const numberArg = (args: Record<string, unknown>, key: string) => {
    const value = Number(args[key])
    return Number.isFinite(value) ? value : null
}

export const organisationTools: McpTool[] = [
    {
        name: "organisation.customers.search",
        title: "Kunden suchen",
        description: "Sucht aktive Kunden des aktiven Mandanten.",
        requiredPermissions: ["organisation.customers.read"],
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Suchtext für Name, Kundennummer, Vorname, Nachname oder Notizen." },
                includeArchived: { type: "boolean", default: false },
                limit: { type: "number", minimum: 1, maximum: 100 },
            },
        },
        async handler(context, args) {
            const conditions = [eq(customers.tenant, context.tenantId)]
            const query = stringArg(args, "query")

            if (query) {
                conditions.push(or(
                    ilike(customers.name, `%${query}%`),
                    ilike(customers.customerNumber, `%${query}%`),
                    ilike(customers.firstname, `%${query}%`),
                    ilike(customers.lastname, `%${query}%`),
                    ilike(customers.notes, `%${query}%`)
                ))
            }
            if (args.includeArchived !== true) conditions.push(eq(customers.archived, false))

            const rows = await context.server.db
                .select({
                    id: customers.id,
                    customerNumber: customers.customerNumber,
                    name: customers.name,
                    firstname: customers.firstname,
                    lastname: customers.lastname,
                    type: customers.type,
                    isCompany: customers.isCompany,
                    active: customers.active,
                    archived: customers.archived,
                })
                .from(customers)
                .where(and(...conditions))
                .orderBy(customers.name)
                .limit(limitFromArgs(args))

            return { rows }
        },
    },
    {
        name: "organisation.projects.list",
        title: "Projekte auflisten",
        description: "Listet Projekte des aktiven Mandanten mit optionalen Filtern.",
        requiredPermissions: ["organisation.projects.read"],
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Suchtext für Name, Projektnummer, Kundenreferenz oder Notizen." },
                customer: { type: "number" },
                activePhase: { type: "string" },
                includeArchived: { type: "boolean", default: false },
                limit: { type: "number", minimum: 1, maximum: 100 },
            },
        },
        async handler(context, args) {
            const conditions = [eq(projects.tenant, context.tenantId)]
            const query = stringArg(args, "query")
            const customer = numberArg(args, "customer")
            const activePhase = stringArg(args, "activePhase")

            if (query) {
                conditions.push(or(
                    ilike(projects.name, `%${query}%`),
                    ilike(projects.projectNumber, `%${query}%`),
                    ilike(projects.customerRef, `%${query}%`),
                    ilike(projects.notes, `%${query}%`)
                ))
            }
            if (customer) conditions.push(eq(projects.customer, customer))
            if (activePhase) conditions.push(eq(projects.active_phase, activePhase))
            if (args.includeArchived !== true) conditions.push(eq(projects.archived, false))

            const rows = await context.server.db
                .select()
                .from(projects)
                .where(and(...conditions))
                .orderBy(desc(projects.createdAt))
                .limit(limitFromArgs(args))

            return { rows }
        },
    },
    {
        name: "organisation.projects.get",
        title: "Projekt laden",
        description: "Lädt ein Projekt des aktiven Mandanten anhand seiner ID.",
        requiredPermissions: ["organisation.projects.read"],
        inputSchema: {
            type: "object",
            required: ["id"],
            properties: {
                id: { type: "number" },
            },
        },
        async handler(context, args) {
            const id = numberArg(args, "id")
            if (!id) throw new Error("id ist erforderlich")

            const rows = await context.server.db
                .select()
                .from(projects)
                .where(and(eq(projects.id, id), eq(projects.tenant, context.tenantId)))
                .limit(1)

            if (!rows[0]) throw new Error("Projekt nicht gefunden")
            return { project: rows[0] }
        },
    },
    {
        name: "organisation.plants.list",
        title: "Anlagen auflisten",
        description: "Listet Anlagen des aktiven Mandanten mit optionalem Kundenfilter.",
        requiredPermissions: ["organisation.plants.read"],
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Suchtext für Name." },
                customer: { type: "number" },
                includeArchived: { type: "boolean", default: false },
                limit: { type: "number", minimum: 1, maximum: 100 },
            },
        },
        async handler(context, args) {
            const conditions = [eq(plants.tenant, context.tenantId)]
            const query = stringArg(args, "query")
            const customer = numberArg(args, "customer")

            if (query) conditions.push(ilike(plants.name, `%${query}%`))
            if (customer) conditions.push(eq(plants.customer, customer))
            if (args.includeArchived !== true) conditions.push(eq(plants.archived, false))

            const rows = await context.server.db
                .select()
                .from(plants)
                .where(and(...conditions))
                .orderBy(plants.name)
                .limit(limitFromArgs(args))

            return { rows }
        },
    },
    {
        name: "organisation.events.list",
        title: "Termine auflisten",
        description: "Listet Termine des aktiven Mandanten mit optionalen Projekt- oder Kundenfiltern.",
        requiredPermissions: ["organisation.events.read"],
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Suchtext für Name, Notizen oder Link." },
                project: { type: "number" },
                customer: { type: "number" },
                eventtype: { type: "string" },
                includeArchived: { type: "boolean", default: false },
                limit: { type: "number", minimum: 1, maximum: 100 },
            },
        },
        async handler(context, args) {
            const conditions = [eq(events.tenant, context.tenantId)]
            const query = stringArg(args, "query")
            const project = numberArg(args, "project")
            const customer = numberArg(args, "customer")
            const eventtype = stringArg(args, "eventtype")

            if (query) {
                conditions.push(or(
                    ilike(events.name, `%${query}%`),
                    ilike(events.notes, `%${query}%`),
                    ilike(events.link, `%${query}%`)
                ))
            }
            if (project) conditions.push(eq(events.project, project))
            if (customer) conditions.push(eq(events.customer, customer))
            if (eventtype) conditions.push(eq(events.eventtype, eventtype))
            if (args.includeArchived !== true) conditions.push(eq(events.archived, false))

            const rows = await context.server.db
                .select()
                .from(events)
                .where(and(...conditions))
                .orderBy(desc(events.startDate))
                .limit(limitFromArgs(args))

            return { rows }
        },
    },
    {
        name: "organisation.tasks.list",
        title: "Aufgaben auflisten",
        description: "Listet Aufgaben des aktiven Mandanten mit optionalen Filtern.",
        requiredPermissions: ["organisation.tasks.read"],
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Suchtext für Name, Beschreibung oder Kategorie." },
                project: { type: "number" },
                customer: { type: "number" },
                includeArchived: { type: "boolean", default: false },
                limit: { type: "number", minimum: 1, maximum: 100 },
            },
        },
        async handler(context, args) {
            const conditions = [eq(tasks.tenant, context.tenantId)]
            const query = stringArg(args, "query")
            const project = numberArg(args, "project")
            const customer = numberArg(args, "customer")

            if (query) {
                conditions.push(or(
                    ilike(tasks.name, `%${query}%`),
                    ilike(tasks.description, `%${query}%`),
                    ilike(tasks.categorie, `%${query}%`)
                ))
            }
            if (project) conditions.push(eq(tasks.project, project))
            if (customer) conditions.push(eq(tasks.customer, customer))
            if (args.includeArchived !== true) conditions.push(eq(tasks.archived, false))

            const rows = await context.server.db
                .select()
                .from(tasks)
                .where(and(...conditions))
                .orderBy(desc(tasks.createdAt))
                .limit(limitFromArgs(args))

            return { rows }
        },
    },
    {
        name: "organisation.tasks.get",
        title: "Aufgabe laden",
        description: "Lädt eine Aufgabe des aktiven Mandanten anhand ihrer ID.",
        requiredPermissions: ["organisation.tasks.read"],
        inputSchema: {
            type: "object",
            required: ["id"],
            properties: {
                id: { type: "number" },
            },
        },
        async handler(context, args) {
            const id = numberArg(args, "id")
            if (!id) throw new Error("id ist erforderlich")

            const rows = await context.server.db
                .select()
                .from(tasks)
                .where(and(eq(tasks.id, id), eq(tasks.tenant, context.tenantId)))
                .limit(1)

            if (!rows[0]) throw new Error("Aufgabe nicht gefunden")
            return { task: rows[0] }
        },
    },
    {
        name: "organisation.tasks.create",
        title: "Aufgabe erstellen",
        description: "Erstellt eine neue Aufgabe im aktiven Mandanten.",
        requiredPermissions: ["organisation.tasks.write"],
        inputSchema: {
            type: "object",
            required: ["name"],
            properties: {
                name: { type: "string" },
                description: { type: "string" },
                categorie: { type: "string" },
                project: { type: "number" },
                plant: { type: "number" },
                customer: { type: "number" },
                userId: { type: "string" },
                profiles: { type: "array", items: { type: "string" } },
            },
        },
        async handler(context, args) {
            const name = stringArg(args, "name")
            if (!name) throw new Error("name ist erforderlich")

            const [created] = await context.server.db
                .insert(tasks)
                .values({
                    name,
                    description: stringArg(args, "description"),
                    categorie: stringArg(args, "categorie"),
                    tenant: context.tenantId,
                    userId: stringArg(args, "userId") || context.userId,
                    project: numberArg(args, "project"),
                    plant: numberArg(args, "plant"),
                    customer: numberArg(args, "customer"),
                    profiles: Array.isArray(args.profiles) ? args.profiles : [],
                    updatedAt: new Date(),
                    updatedBy: context.userId,
                })
                .returning()

            return { task: created }
        },
    },
    {
        name: "organisation.tasks.update",
        title: "Aufgabe aktualisieren",
        description: "Aktualisiert Felder einer Aufgabe im aktiven Mandanten.",
        requiredPermissions: ["organisation.tasks.write"],
        inputSchema: {
            type: "object",
            required: ["id"],
            properties: {
                id: { type: "number" },
                name: { type: "string" },
                description: { type: "string" },
                categorie: { type: "string" },
                project: { type: "number" },
                plant: { type: "number" },
                customer: { type: "number" },
                userId: { type: "string" },
                profiles: { type: "array", items: { type: "string" } },
                archived: { type: "boolean" },
            },
        },
        async handler(context, args) {
            const id = numberArg(args, "id")
            if (!id) throw new Error("id ist erforderlich")

            const update: Record<string, unknown> = {
                updatedAt: new Date(),
                updatedBy: context.userId,
            }

            for (const key of ["name", "description", "categorie", "userId"] as const) {
                if (args[key] !== undefined) update[key] = stringArg(args, key)
            }
            for (const key of ["project", "plant", "customer"] as const) {
                if (args[key] !== undefined) update[key] = numberArg(args, key)
            }
            if (Array.isArray(args.profiles)) update.profiles = args.profiles
            if (typeof args.archived === "boolean") update.archived = args.archived

            const [updated] = await context.server.db
                .update(tasks)
                .set(update)
                .where(and(eq(tasks.id, id), eq(tasks.tenant, context.tenantId)))
                .returning()

            if (!updated) throw new Error("Aufgabe nicht gefunden")
            return { task: updated }
        },
    },
    {
        name: "organisation.tasks.archive",
        title: "Aufgabe archivieren",
        description: "Archiviert eine Aufgabe im aktiven Mandanten.",
        requiredPermissions: ["organisation.tasks.write"],
        inputSchema: {
            type: "object",
            required: ["id"],
            properties: {
                id: { type: "number" },
            },
        },
        async handler(context, args) {
            const id = numberArg(args, "id")
            if (!id) throw new Error("id ist erforderlich")

            const [updated] = await context.server.db
                .update(tasks)
                .set({
                    archived: true,
                    updatedAt: new Date(),
                    updatedBy: context.userId,
                })
                .where(and(eq(tasks.id, id), eq(tasks.tenant, context.tenantId)))
                .returning()

            if (!updated) throw new Error("Aufgabe nicht gefunden")
            return { task: updated }
        },
    },
]
