import { call, os } from "@orpc/server";
import { z } from "zod";
import { createKV } from "../lib/create-kv.js";
import { assertOwner, sessionsKV } from "../lib/auth.js";
// Schema Definition
const ReviewSchema = z.object({
    id: z.string(),
    bookingId: z.string(),
    customerName: z.string().min(2, "Kundenname muss mindestens 2 Zeichen lang sein"),
    customerEmail: z.string().email("Ungültige E-Mail-Adresse").optional(),
    rating: z.number().int().min(1).max(5),
    comment: z.string().min(10, "Kommentar muss mindestens 10 Zeichen lang sein"),
    status: z.enum(["pending", "approved", "rejected"]),
    createdAt: z.string(),
    reviewedAt: z.string().optional(),
    reviewedBy: z.string().optional(),
});
// KV Storage
const reviewsKV = createKV("reviews");
const cancellationKV = createKV("cancellation_tokens");
const bookingsKV = createKV("bookings");
// Helper Function: validateBookingToken
async function validateBookingToken(token) {
    const tokens = await cancellationKV.getAllItems();
    const validToken = tokens.find(t => t.token === token &&
        new Date(t.expiresAt) > new Date() &&
        t.purpose === 'booking_access');
    if (!validToken) {
        throw new Error("Ungültiger oder abgelaufener Buchungs-Token");
    }
    const booking = await bookingsKV.getItem(validToken.bookingId);
    if (!booking) {
        throw new Error("Buchung nicht gefunden");
    }
    // Only allow reviews for completed appointments
    if (!(booking.status === "completed")) {
        throw new Error("Bewertungen sind nur für abgeschlossene Termine möglich");
    }
    return booking;
}
// Public Endpoint: submitReview
const submitReview = os
    .input(z.object({
    bookingToken: z.string(),
    rating: z.number().int().min(1).max(5),
    comment: z.string().min(10, "Kommentar muss mindestens 10 Zeichen lang sein"),
}))
    .handler(async ({ input }) => {
    try {
        // Validate bookingToken
        const booking = await validateBookingToken(input.bookingToken);
        // Enforce uniqueness by using booking.id as the KV key
        const existing = await reviewsKV.getItem(booking.id);
        if (existing) {
            throw new Error("Für diese Buchung wurde bereits eine Bewertung abgegeben");
        }
        // Create review object
        const review = {
            id: booking.id,
            bookingId: booking.id,
            customerName: booking.customerName,
            customerEmail: booking.customerEmail,
            rating: input.rating,
            comment: input.comment,
            status: "pending",
            createdAt: new Date().toISOString(),
        };
        await reviewsKV.setItem(booking.id, review);
        return review;
    }
    catch (err) {
        console.error("reviews.submitReview error", err);
        throw err;
    }
});
// Admin Endpoint: approveReview
const approveReview = os
    .input(z.object({ sessionId: z.string(), id: z.string() }))
    .handler(async ({ input }) => {
    try {
        await assertOwner(input.sessionId);
        const review = await reviewsKV.getItem(input.id);
        if (!review) {
            throw new Error("Bewertung nicht gefunden");
        }
        const session = await sessionsKV.getItem(input.sessionId).catch(() => undefined);
        const updatedReview = {
            ...review,
            status: "approved",
            reviewedAt: new Date().toISOString(),
            reviewedBy: session?.userId || review.reviewedBy,
        };
        await reviewsKV.setItem(input.id, updatedReview);
        return updatedReview;
    }
    catch (err) {
        console.error("reviews.approveReview error", err);
        throw err;
    }
});
// Admin Endpoint: rejectReview
const rejectReview = os
    .input(z.object({ sessionId: z.string(), id: z.string() }))
    .handler(async ({ input }) => {
    try {
        await assertOwner(input.sessionId);
        const review = await reviewsKV.getItem(input.id);
        if (!review) {
            throw new Error("Bewertung nicht gefunden");
        }
        const session = await sessionsKV.getItem(input.sessionId).catch(() => undefined);
        const updatedReview = {
            ...review,
            status: "rejected",
            reviewedAt: new Date().toISOString(),
            reviewedBy: session?.userId || review.reviewedBy,
        };
        await reviewsKV.setItem(input.id, updatedReview);
        return updatedReview;
    }
    catch (err) {
        console.error("reviews.rejectReview error", err);
        throw err;
    }
});
// Admin Endpoint: deleteReview
const deleteReview = os
    .input(z.object({ sessionId: z.string(), id: z.string() }))
    .handler(async ({ input }) => {
    try {
        await assertOwner(input.sessionId);
        await reviewsKV.removeItem(input.id);
    }
    catch (err) {
        console.error("reviews.deleteReview error", err);
        throw err;
    }
});
// Public Endpoint: listPublishedReviews
const listPublishedReviews = os.handler(async () => {
    try {
        const allReviews = await reviewsKV.getAllItems();
        const published = allReviews.filter(r => r.status === "approved");
        const sorted = published.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
        const publicSafe = sorted.map(r => ({
            customerName: r.customerName,
            rating: r.rating,
            comment: r.comment,
            status: r.status,
            bookingId: r.bookingId,
            createdAt: r.createdAt,
        }));
        return publicSafe;
    }
    catch (err) {
        console.error("reviews.listPublishedReviews error", err);
        throw err;
    }
});
// Admin Endpoint: adminListReviews
const adminListReviews = os
    .input(z.object({
    sessionId: z.string(),
    statusFilter: z.enum(["all", "pending", "approved", "rejected"]).optional().default("all"),
}))
    .handler(async ({ input }) => {
    try {
        await assertOwner(input.sessionId);
        const allReviews = await reviewsKV.getAllItems();
        const filtered = input.statusFilter === "all"
            ? allReviews
            : allReviews.filter(r => r.status === input.statusFilter);
        const sorted = filtered.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
        return sorted;
    }
    catch (err) {
        console.error("reviews.adminListReviews error", err);
        throw err;
    }
});
// Live Queries
const live = {
    listPublishedReviews: os.handler(async function* ({ signal }) {
        yield call(listPublishedReviews, {}, { signal });
        for await (const _ of reviewsKV.subscribe()) {
            yield call(listPublishedReviews, {}, { signal });
        }
    }),
    adminListReviews: os
        .input(z.object({
        sessionId: z.string(),
        statusFilter: z.enum(["all", "pending", "approved", "rejected"]).optional().default("all"),
    }))
        .handler(async function* ({ input, signal }) {
        await assertOwner(input.sessionId);
        const allReviews = await reviewsKV.getAllItems();
        const filtered = input.statusFilter === "all"
            ? allReviews
            : allReviews.filter(r => r.status === input.statusFilter);
        const sorted = filtered.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
        yield sorted;
        for await (const _ of reviewsKV.subscribe()) {
            const updated = await reviewsKV.getAllItems();
            const filteredUpdated = input.statusFilter === "all"
                ? updated
                : updated.filter(r => r.status === input.statusFilter);
            const sortedUpdated = filteredUpdated.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
            yield sortedUpdated;
        }
    }),
};
export const router = {
    submitReview,
    approveReview,
    rejectReview,
    deleteReview,
    listPublishedReviews,
    adminListReviews,
    live,
};
