import { call, os } from "@orpc/server";
import { z } from "zod";
import { createKV } from "@/server/lib/create-kv";
import { createKV as createAvailabilityKV } from "@/server/lib/create-kv";
import { randomUUID } from "crypto";

// Schema for cancellation token
const CancellationTokenSchema = z.object({
  id: z.string(),
  bookingId: z.string(),
  token: z.string(),
  expiresAt: z.string(),
  createdAt: z.string(),
});

type CancellationToken = z.output<typeof CancellationTokenSchema>;

const cancellationKV = createKV<CancellationToken>("cancellation_tokens");

// Types for booking and availability
type Booking = {
  id: string;
  treatmentId: string;
  customerName: string;
  customerEmail: string;
  customerPhone: string;
  appointmentDate: string;
  appointmentTime: string;
  notes?: string;
  inspirationPhoto?: string;
  slotId?: string;
  status: "pending" | "confirmed" | "cancelled" | "completed";
  createdAt: string;
};

type Availability = {
  id: string;
  date: string;
  time: string;
  durationMinutes: number;
  status: "free" | "reserved";
  reservedByBookingId?: string;
  createdAt: string;
};

const bookingsKV = createKV<Booking>("bookings");
const availabilityKV = createAvailabilityKV<Availability>("availability");

// Helper function to convert date from yyyy-mm-dd to dd.mm.yyyy
function formatDateGerman(dateString: string): string {
  const [year, month, day] = dateString.split('-');
  return `${day}.${month}.${year}`;
}

// Create cancellation token for a booking
const createToken = os
  .input(z.object({ bookingId: z.string() }))
  .handler(async ({ input }) => {
    const booking = await bookingsKV.getItem(input.bookingId);
    if (!booking) {
      throw new Error("Booking not found");
    }
    
    if (booking.status === "cancelled") {
      throw new Error("Booking is already cancelled");
    }
    
    // Create token that expires in 30 days
    const expiresAt = new Date();
    expiresAt.setDate(expiresAt.getDate() + 30);
    
    const token = randomUUID();
    const cancellationToken: CancellationToken = {
      id: randomUUID(),
      bookingId: input.bookingId,
      token,
      expiresAt: expiresAt.toISOString(),
      createdAt: new Date().toISOString(),
    };
    
    await cancellationKV.setItem(cancellationToken.id, cancellationToken);
    return { token, expiresAt: expiresAt.toISOString() };
  });

// Get booking details by token
const getBookingByToken = os
  .input(z.object({ token: z.string() }))
  .handler(async ({ input }) => {
    const tokens = await cancellationKV.getAllItems();
    const validToken = tokens.find(t => 
      t.token === input.token && 
      new Date(t.expiresAt) > new Date()
    );
    
    if (!validToken) {
      throw new Error("Invalid or expired cancellation token");
    }
    
    const booking = await bookingsKV.getItem(validToken.bookingId);
    if (!booking) {
      throw new Error("Booking not found");
    }
    
    // Get treatment details
    const treatmentsKV = createKV<any>("treatments");
    const treatment = await treatmentsKV.getItem(booking.treatmentId);
    
    return {
      id: booking.id,
      customerName: booking.customerName,
      appointmentDate: booking.appointmentDate,
      appointmentTime: booking.appointmentTime,
      treatmentId: booking.treatmentId,
      treatmentName: treatment?.name || "Unbekannte Behandlung",
      status: booking.status,
      formattedDate: formatDateGerman(booking.appointmentDate),
    };
  });

// Cancel booking by token
const cancelByToken = os
  .input(z.object({ token: z.string() }))
  .handler(async ({ input }) => {
    const tokens = await cancellationKV.getAllItems();
    const validToken = tokens.find(t => 
      t.token === input.token && 
      new Date(t.expiresAt) > new Date()
    );
    
    if (!validToken) {
      throw new Error("Invalid or expired cancellation token");
    }
    
    const booking = await bookingsKV.getItem(validToken.bookingId);
    if (!booking) {
      throw new Error("Booking not found");
    }
    
    // Check if booking is already cancelled
    if (booking.status === "cancelled") {
      throw new Error("Booking is already cancelled");
    }
    
    // Check minimum cancellation timespan from environment variable
    const minStornoTimespan = parseInt(process.env.MIN_STORNO_TIMESPAN || "24"); // Default: 24 hours
    const appointmentDateTime = new Date(`${booking.appointmentDate}T${booking.appointmentTime}:00`);
    const now = new Date();
    const timeDifferenceHours = (appointmentDateTime.getTime() - now.getTime()) / (1000 * 60 * 60);
    
    if (timeDifferenceHours < minStornoTimespan) {
      throw new Error(`Stornierung ist nur bis ${minStornoTimespan} Stunden vor dem Termin möglich. Der Termin liegt nur noch ${Math.round(timeDifferenceHours)} Stunden in der Zukunft.`);
    }
    
    // Check if booking is in the past (additional safety check)
    const today = new Date().toISOString().split("T")[0];
    if (booking.appointmentDate < today) {
      throw new Error("Cannot cancel past bookings");
    }
    
    // For today's bookings, check if the time is not in the past
    if (booking.appointmentDate === today) {
      const currentTime = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
      if (booking.appointmentTime <= currentTime) {
        throw new Error("Cannot cancel bookings that have already started");
      }
    }
    
    // Update booking status
    const updatedBooking = { ...booking, status: "cancelled" as const };
    await bookingsKV.setItem(booking.id, updatedBooking);
    
    // Free the slot if it exists
    if (booking.slotId) {
      const slot = await availabilityKV.getItem(booking.slotId);
      if (slot) {
        const updatedSlot: Availability = {
          ...slot,
          status: "free",
          reservedByBookingId: undefined,
        };
        await availabilityKV.setItem(slot.id, updatedSlot);
      }
    }
    
    // Invalidate the token
    await cancellationKV.removeItem(validToken.id);
    
    return {
      success: true,
      message: "Booking cancelled successfully",
      formattedDate: formatDateGerman(booking.appointmentDate),
    };
  });

export const router = {
  createToken,
  getBookingByToken,
  cancelByToken,
};
