import crypto from "crypto"
import { and, asc, eq, sql } from "drizzle-orm"
import { FastifyInstance } from "fastify"

import { authProfiles, events } from "../../db/schema"
import { secrets } from "./secrets"

const CALENDAR_FEED_PREFIX = "/api/public/calendar/subscriptions"

function escapeIcsText(value: string) {
    return value
        .replace(/\\/g, "\\\\")
        .replace(/\r?\n/g, "\\n")
        .replace(/;/g, "\\;")
        .replace(/,/g, "\\,")
}

function foldIcsLine(line: string) {
    const maxLength = 73
    if (line.length <= maxLength) return line

    const parts: string[] = []
    for (let index = 0; index < line.length; index += maxLength) {
        parts.push(index === 0 ? line.slice(index, index + maxLength) : ` ${line.slice(index, index + maxLength)}`)
    }

    return parts.join("\r\n")
}

function formatUtcDate(value: string | Date | null | undefined) {
    if (!value) return null

    const date = new Date(value)
    if (Number.isNaN(date.getTime())) return null

    return date
        .toISOString()
        .replace(/[-:]/g, "")
        .replace(/\.\d{3}Z$/, "Z")
}

function buildRecurrenceRule(repeatInterval?: string | null) {
    switch (repeatInterval) {
        case "Täglich":
            return "FREQ=DAILY"
        case "Wöchentlich":
            return "FREQ=WEEKLY"
        case "2-wöchentlich":
            return "FREQ=WEEKLY;INTERVAL=2"
        case "Monatlich":
            return "FREQ=MONTHLY"
        case "Jährlich":
            return "FREQ=YEARLY"
        default:
            return null
    }
}

function normalizeApiBaseUrl() {
    const rawBase = secrets.API_BASE_URL?.trim()
    if (!rawBase) return null

    return rawBase.replace(/\/+$/, "")
}

export function buildCalendarSubscriptionPath(token: string) {
    return `${CALENDAR_FEED_PREFIX}/${token}.ics`
}

export function buildCalendarSubscriptionUrl(token: string) {
    const apiBaseUrl = normalizeApiBaseUrl()
    const subscriptionPath = buildCalendarSubscriptionPath(token)

    if (!apiBaseUrl) return subscriptionPath

    if (apiBaseUrl.endsWith("/api")) {
        return `${apiBaseUrl}${subscriptionPath.replace(/^\/api/, "")}`
    }

    return `${apiBaseUrl}${subscriptionPath}`
}

export function enrichProfileWithCalendarSubscription(profile: Record<string, any> | null) {
    if (!profile) return profile

    return {
        ...profile,
        calendar_subscription_path: profile.calendar_subscription_token
            ? buildCalendarSubscriptionPath(profile.calendar_subscription_token)
            : null,
        calendar_subscription_url: profile.calendar_subscription_token
            ? buildCalendarSubscriptionUrl(profile.calendar_subscription_token)
            : null,
    }
}

export async function generateProfileCalendarSubscriptionToken(server: FastifyInstance, profileId: string, tenantId: number) {
    const token = crypto.randomBytes(24).toString("hex")

    const [profile] = await server.db
        .update(authProfiles)
        .set({
            calendar_subscription_token: token,
        })
        .where(
            and(
                eq(authProfiles.id, profileId),
                eq(authProfiles.tenant_id, tenantId)
            )
        )
        .returning()

    return profile || null
}

export async function loadProfileByCalendarSubscriptionToken(server: FastifyInstance, token: string) {
    return server.db.query.authProfiles.findFirst({
        where: eq(authProfiles.calendar_subscription_token, token)
    })
}

export async function buildProfileCalendarSubscriptionFeed(server: FastifyInstance, profile: typeof authProfiles.$inferSelect) {
    const assignedEvents = await server.db
        .select({
            id: events.id,
            name: events.name,
            startDate: events.startDate,
            endDate: events.endDate,
            repeatInterval: events.repeatInterval,
            notes: events.notes,
            link: events.link,
            state: events.state,
            color: events.color,
            createdAt: events.createdAt,
            updatedAt: events.updatedAt
        })
        .from(events)
        .where(
            and(
                eq(events.tenant, profile.tenant_id),
                eq(events.archived, false),
                sql`${events.profiles} ? ${profile.id}`
            )
        )
        .orderBy(asc(events.startDate))

    const nowStamp = formatUtcDate(new Date()) || ""
    const calendarLines = [
        "BEGIN:VCALENDAR",
        "VERSION:2.0",
        "PRODID:-//FEDEO//Kalender-Abo//DE",
        "CALSCALE:GREGORIAN",
        "METHOD:PUBLISH",
        foldIcsLine(`X-WR-CALNAME:${escapeIcsText(`FEDEO - ${profile.full_name}`)}`),
        foldIcsLine(`X-WR-CALDESC:${escapeIcsText(`Kalender-Abo für ${profile.full_name}`)}`),
    ]

    assignedEvents.forEach((event) => {
        const startDate = formatUtcDate(event.startDate)
        if (!startDate) return

        const endDate = formatUtcDate(event.endDate)
        const lastModified = formatUtcDate(event.updatedAt || event.createdAt) || nowStamp
        const recurrenceRule = buildRecurrenceRule(event.repeatInterval)
        const descriptionParts = [event.notes, event.link].filter(Boolean)
        const summary = event.state === "Entwurf" ? `[Entwurf] ${event.name}` : event.name

        calendarLines.push("BEGIN:VEVENT")
        calendarLines.push(foldIcsLine(`UID:fedeo-event-${event.id}@fedeo.local`))
        calendarLines.push(`DTSTAMP:${nowStamp}`)
        calendarLines.push(`DTSTART:${startDate}`)
        if (endDate) {
            calendarLines.push(`DTEND:${endDate}`)
        }
        calendarLines.push(`LAST-MODIFIED:${lastModified}`)
        calendarLines.push(foldIcsLine(`SUMMARY:${escapeIcsText(summary)}`))
        calendarLines.push(`STATUS:${event.state === "Entwurf" ? "TENTATIVE" : "CONFIRMED"}`)
        if (event.color) {
            calendarLines.push(foldIcsLine(`COLOR:${escapeIcsText(event.color)}`))
        }
        if (descriptionParts.length) {
            calendarLines.push(foldIcsLine(`DESCRIPTION:${escapeIcsText(descriptionParts.join("\n\n"))}`))
        }
        if (recurrenceRule) {
            calendarLines.push(`RRULE:${recurrenceRule}`)
        }
        calendarLines.push("END:VEVENT")
    })

    calendarLines.push("END:VCALENDAR")

    return `${calendarLines.join("\r\n")}\r\n`
}
