import { call, os } from "@orpc/server";
import { z } from "zod";
import { randomUUID } from "crypto";
import { createKV } from "@/server/lib/create-kv";
import { createKV as createAvailabilityKV } from "@/server/lib/create-kv";
import { sendEmail, sendEmailWithAGB, sendEmailWithInspirationPhoto } from "@/server/lib/email";
import { renderBookingPendingHTML, renderBookingConfirmedHTML, renderBookingCancelledHTML, renderAdminBookingNotificationHTML } from "@/server/lib/email-templates";
import { router } from "@/server/rpc";
import { createORPCClient } from "@orpc/client";
import { RPCLink } from "@orpc/client/fetch";

// Create a server-side client to call other RPC endpoints
const link = new RPCLink({ url: "http://localhost:5173/rpc" });
const queryClient = createORPCClient<typeof router>(link);

// 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}`;
}

// Helper function to generate URLs from DOMAIN environment variable
function generateUrl(path: string = ''): string {
  const domain = process.env.DOMAIN || 'localhost:5173';
  const protocol = domain.includes('localhost') ? 'http' : 'https';
  return `${protocol}://${domain}${path}`;
}

const BookingSchema = z.object({
  id: z.string(),
  treatmentId: z.string(),
  customerName: z.string(),
  customerEmail: z.string(),
  customerPhone: z.string(),
  appointmentDate: z.string(), // ISO date string
  appointmentTime: z.string(), // HH:MM format
  status: z.enum(["pending", "confirmed", "cancelled", "completed"]),
  notes: z.string().optional(),
  inspirationPhoto: z.string().optional(), // Base64 encoded image data
  createdAt: z.string(),
  slotId: z.string().optional(),
});

type Booking = z.output<typeof BookingSchema>;

const kv = createKV<Booking>("bookings");
type Availability = {
  id: string;
  date: string;
  time: string;
  durationMinutes: number;
  status: "free" | "reserved";
  reservedByBookingId?: string;
  createdAt: string;
};
const availabilityKV = createAvailabilityKV<Availability>("availability");

// Import treatments KV for admin notifications
import { createKV as createTreatmentsKV } from "@/server/lib/create-kv";
type Treatment = {
  id: string;
  name: string;
  description: string;
  price: number;
  duration: number;
  category: string;
  createdAt: string;
};
const treatmentsKV = createTreatmentsKV<Treatment>("treatments");

const create = os
  .input(BookingSchema.omit({ id: true, createdAt: true, status: true }))
  .handler(async ({ input }) => {
    // console.log("Booking create called with input:", {
    //   ...input,
    //   inspirationPhoto: input.inspirationPhoto ? `[${input.inspirationPhoto.length} chars]` : null
    // });
    
    try {
    // Validate that the booking is not in the past
    const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
    if (input.appointmentDate < today) {
      throw new Error("Buchungen für vergangene Termine sind nicht möglich.");
    }
    
    // For today's bookings, check if the time is not in the past
    if (input.appointmentDate === today) {
      const now = new Date();
      const currentTime = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
      if (input.appointmentTime <= currentTime) {
        throw new Error("Buchungen für vergangene Uhrzeiten sind nicht möglich.");
      }
    }
    
    // Prevent double booking: same customer email with pending/confirmed on same date
    const existing = await kv.getAllItems();
    const hasConflict = existing.some(b =>
      b.customerEmail.toLowerCase() === input.customerEmail.toLowerCase() &&
      b.appointmentDate === input.appointmentDate &&
      (b.status === "pending" || b.status === "confirmed")
    );
    if (hasConflict) {
      throw new Error("Du hast bereits eine Buchung für dieses Datum. Bitte wähle einen anderen Tag oder storniere zuerst.");
    }
    const id = randomUUID();
    const booking = { 
      id, 
      ...input, 
      status: "pending" as const,
      createdAt: new Date().toISOString()
    };
    // If a slotId is provided, tentatively reserve the slot (mark reserved but pending)
    if (booking.slotId) {
      const slot = await availabilityKV.getItem(booking.slotId);
      if (!slot) throw new Error("Availability slot not found");
      if (slot.status !== "free") throw new Error("Slot not available");
      const updatedSlot: Availability = {
        ...slot,
        status: "reserved",
        reservedByBookingId: id,
      };
      await availabilityKV.setItem(slot.id, updatedSlot);
    }
    await kv.setItem(id, booking);

    // Notify customer: request received (pending)
    void (async () => {
      const formattedDate = formatDateGerman(input.appointmentDate);
      const homepageUrl = generateUrl();
      const html = await renderBookingPendingHTML({ name: input.customerName, date: input.appointmentDate, time: input.appointmentTime });
      await sendEmail({
      to: input.customerEmail,
      subject: "Deine Terminanfrage ist eingegangen",
      text: `Hallo ${input.customerName},\n\nwir haben deine Anfrage für ${formattedDate} um ${input.appointmentTime} erhalten. Wir bestätigen deinen Termin in Kürze.\n\nRechtliche Informationen: ${generateUrl('/legal')}\nZur Website: ${homepageUrl}\n\nLiebe Grüße\nStargirlnails Kiel`,
        html,
      }).catch(() => {});
    })();

    // Notify admin: new booking request (with photo if available)
    void (async () => {
      if (!process.env.ADMIN_EMAIL) return;
      
      // Get treatment name from KV
      const allTreatments = await treatmentsKV.getAllItems();
      const treatment = allTreatments.find(t => t.id === input.treatmentId);
      const treatmentName = treatment?.name || "Unbekannte Behandlung";
      
      const adminHtml = await renderAdminBookingNotificationHTML({
        name: input.customerName,
        date: input.appointmentDate,
        time: input.appointmentTime,
        treatment: treatmentName,
        phone: input.customerPhone,
        notes: input.notes,
        hasInspirationPhoto: !!input.inspirationPhoto
      });

      const homepageUrl = generateUrl();
      
      const adminText = `Neue Buchungsanfrage eingegangen:\n\n` +
        `Name: ${input.customerName}\n` +
        `Telefon: ${input.customerPhone}\n` +
        `Behandlung: ${treatmentName}\n` +
        `Datum: ${formatDateGerman(input.appointmentDate)}\n` +
        `Uhrzeit: ${input.appointmentTime}\n` +
        `${input.notes ? `Notizen: ${input.notes}\n` : ''}` +
        `Inspiration-Foto: ${input.inspirationPhoto ? 'Im Anhang verfügbar' : 'Kein Foto hochgeladen'}\n\n` +
        `Zur Website: ${homepageUrl}\n\n` +
        `Bitte logge dich in das Admin-Panel ein, um die Buchung zu bearbeiten.`;

      if (input.inspirationPhoto) {
        await sendEmailWithInspirationPhoto({
          to: process.env.ADMIN_EMAIL,
          subject: `Neue Buchungsanfrage - ${input.customerName}`,
          text: adminText,
          html: adminHtml,
        }, input.inspirationPhoto, input.customerName).catch(() => {});
      } else {
        await sendEmail({
          to: process.env.ADMIN_EMAIL,
          subject: `Neue Buchungsanfrage - ${input.customerName}`,
          text: adminText,
          html: adminHtml,
        }).catch(() => {});
      }
    })();
    return booking;
    } catch (error) {
      console.error("Booking creation error:", error);
      throw error;
    }
  });

// Owner check reuse (simple inline version)
type Session = { id: string; userId: string; expiresAt: string; createdAt: string };
type User = { id: string; username: string; email: string; passwordHash: string; role: "customer" | "owner"; createdAt: string };
const sessionsKV = createKV<Session>("sessions");
const usersKV = createKV<User>("users");
async function assertOwner(sessionId: string): Promise<void> {
  const session = await sessionsKV.getItem(sessionId);
  if (!session) throw new Error("Invalid session");
  if (new Date(session.expiresAt) < new Date()) throw new Error("Session expired");
  const user = await usersKV.getItem(session.userId);
  if (!user || user.role !== "owner") throw new Error("Forbidden");
}

const updateStatus = os
  .input(z.object({
    sessionId: z.string(),
    id: z.string(),
    status: z.enum(["pending", "confirmed", "cancelled", "completed"])
  }))
  .handler(async ({ input }) => {
    await assertOwner(input.sessionId);
    const booking = await kv.getItem(input.id);
    if (!booking) throw new Error("Booking not found");
    
    const previousStatus = booking.status;
    const updatedBooking = { ...booking, status: input.status };
    await kv.setItem(input.id, updatedBooking);

    // Manage availability slot state transitions
    if (booking.slotId) {
      const slot = await availabilityKV.getItem(booking.slotId);
      if (slot) {
        // console.log(`Updating slot ${slot.id} (${slot.date} ${slot.time}) from ${slot.status} to ${input.status}`);
        
        if (input.status === "cancelled") {
          // Free the slot again
          await availabilityKV.setItem(slot.id, {
            ...slot,
            status: "free",
            reservedByBookingId: undefined,
          });
          // console.log(`Slot ${slot.id} freed due to cancellation`);
        } else if (input.status === "pending") {
          // keep reserved as pending
          if (slot.status !== "reserved") {
            await availabilityKV.setItem(slot.id, {
              ...slot,
              status: "reserved",
              reservedByBookingId: booking.id,
            });
            // console.log(`Slot ${slot.id} reserved for pending booking`);
          }
        } else if (input.status === "confirmed" || input.status === "completed") {
          // keep reserved; optionally noop
          if (slot.status !== "reserved" || slot.reservedByBookingId !== booking.id) {
            await availabilityKV.setItem(slot.id, {
              ...slot,
              status: "reserved",
              reservedByBookingId: booking.id,
            });
            // console.log(`Slot ${slot.id} confirmed as reserved`);
          }
        }
      }
    }
    // Email notifications on status changes
    try {
      if (input.status === "confirmed") {
        // Create cancellation token for this booking
        const cancellationToken = await queryClient.cancellation.createToken({ bookingId: booking.id });
        
        const formattedDate = formatDateGerman(booking.appointmentDate);
        const cancellationUrl = generateUrl(`/cancel/${cancellationToken.token}`);
        const homepageUrl = generateUrl();
        
        const html = await renderBookingConfirmedHTML({ 
          name: booking.customerName, 
          date: booking.appointmentDate, 
          time: booking.appointmentTime,
          cancellationUrl 
        });
        
        await sendEmailWithAGB({
          to: booking.customerEmail,
          subject: "Dein Termin wurde bestätigt - AGB im Anhang",
          text: `Hallo ${booking.customerName},\n\nwir haben deinen Termin am ${formattedDate} um ${booking.appointmentTime} bestätigt.\n\nWichtiger Hinweis: Die Allgemeinen Geschäftsbedingungen (AGB) findest du im Anhang dieser E-Mail. Bitte lies sie vor deinem Termin durch.\n\nFalls du den Termin stornieren möchtest, kannst du das hier tun: ${cancellationUrl}\n\nRechtliche Informationen: ${generateUrl('/legal')}\nZur Website: ${homepageUrl}\n\nBis bald!\nStargirlnails Kiel`,
          html,
          cc: process.env.ADMIN_EMAIL ? [process.env.ADMIN_EMAIL] : undefined,
        });
      } else if (input.status === "cancelled") {
        const formattedDate = formatDateGerman(booking.appointmentDate);
        const homepageUrl = generateUrl();
        const html = await renderBookingCancelledHTML({ name: booking.customerName, date: booking.appointmentDate, time: booking.appointmentTime });
        await sendEmail({
          to: booking.customerEmail,
          subject: "Dein Termin wurde abgesagt",
          text: `Hallo ${booking.customerName},\n\nleider wurde dein Termin am ${formattedDate} um ${booking.appointmentTime} abgesagt. Bitte buche einen neuen Termin.\n\nRechtliche Informationen: ${generateUrl('/legal')}\nZur Website: ${homepageUrl}\n\nLiebe Grüße\nStargirlnails Kiel`,
          html,
          cc: process.env.ADMIN_EMAIL ? [process.env.ADMIN_EMAIL] : undefined,
        });
      }
    } catch (e) {
      console.error("Email send failed:", e);
    }
    return updatedBooking;
  });

const remove = os.input(z.string()).handler(async ({ input }) => {
  await kv.removeItem(input);
});

const list = os.handler(async () => {
  return kv.getAllItems();
});

const get = os.input(z.string()).handler(async ({ input }) => {
  return kv.getItem(input);
});

const getByDate = os
  .input(z.string()) // YYYY-MM-DD format
  .handler(async ({ input }) => {
    const allBookings = await kv.getAllItems();
    return allBookings.filter(booking => booking.appointmentDate === input);
  });

const live = {
  list: os.handler(async function* ({ signal }) {
    yield call(list, {}, { signal });
    for await (const _ of kv.subscribe()) {
      yield call(list, {}, { signal });
    }
  }),
  
  byDate: os
    .input(z.string())
    .handler(async function* ({ input, signal }) {
      yield call(getByDate, input, { signal });
      for await (const _ of kv.subscribe()) {
        yield call(getByDate, input, { signal });
      }
    }),
};

export const router = {
  create,
  updateStatus,
  remove,
  list,
  get,
  getByDate,
  live,
};