/**
 * Calendar sync utility for real estate property viewings.
 *
 * Syncs a new viewing to the user's connected calendar (Google or Outlook).
 * This is a non-blocking operation — failures are logged but don't affect the viewing.
 */

import { prisma } from "@/lib/db";
import { bookingNotesAdapter } from "@/lib/adapters/bookings";
import { logger } from "@/lib/logger";
import { getErrorMessage } from "@/lib/errors";
import {
  hasGoogleCalendarConnection,
  listUserCalendars as listGoogleCalendars,
  createCalendarEvent as createGoogleCalendarEvent,
  type CreateEventInput as GoogleEventInput,
} from "@/lib/services/google-calendar-user.service";
import {
  hasOutlookConnection,
  listUserCalendars as listOutlookCalendars,
  createCalendarEvent as createOutlookCalendarEvent,
  type CreateOutlookEventInput,
} from "@/lib/services/outlook-calendar.service";

/** Default timezone for GCC region */
const DEFAULT_CALENDAR_TIMEZONE = "Asia/Qatar";

export interface ViewingCalendarEvent {
  viewingId: string;
  propertyTitle: string;
  propertyAddress: string; // district, city
  clientName: string;
  clientPhone: string;
  clientEmail?: string | null;
  brokerName?: string;
  brokerPhone?: string;
  viewingDate: Date;
  viewingTime: string; // HH:MM
  duration: number; // minutes
  viewingType: string;
  notes?: string | null;
  meetingLink?: string | null;
}

/**
 * Build the event description body from viewing details.
 */
function buildViewingDescription(viewing: ViewingCalendarEvent): string {
  const lines: string[] = [
    `Property Viewing - ${viewing.viewingType}`,
    "",
    `Client: ${viewing.clientName}`,
    `Phone: ${viewing.clientPhone}`,
  ];

  if (viewing.clientEmail) {
    lines.push(`Email: ${viewing.clientEmail}`);
  }

  lines.push("");

  if (viewing.brokerName) {
    lines.push(`Broker: ${viewing.brokerName}`);
  }
  if (viewing.brokerPhone) {
    lines.push(`Broker Phone: ${viewing.brokerPhone}`);
  }

  lines.push("");
  lines.push(`Property: ${viewing.propertyTitle}`);
  lines.push(`Location: ${viewing.propertyAddress}`);

  if (viewing.notes) {
    lines.push("");
    lines.push(`Notes: ${viewing.notes}`);
  }

  if (viewing.meetingLink) {
    lines.push("");
    lines.push(`Meeting Link: ${viewing.meetingLink}`);
  }

  lines.push("");
  lines.push("Created via Mawidi Real Estate");

  return lines.join("\n");
}

/**
 * Sync a property viewing to the user's connected calendar.
 *
 * Checks Google Calendar first, falls back to Outlook. If neither is
 * connected, logs a debug message and returns silently.
 *
 * This function is non-blocking — all errors are caught and logged,
 * never thrown.
 */
export async function syncViewingToCalendar(
  userId: string,
  viewing: ViewingCalendarEvent,
): Promise<void> {
  try {
    // Get user's timezone from preferences, fallback to GCC default
    const preferences = await prisma.user_preferences.findUnique({
      where: { userId },
      select: { timezone: true },
    });
    const timeZone = preferences?.timezone || DEFAULT_CALENDAR_TIMEZONE;

    // Calculate start and end datetime
    const [hours, minutes] = viewing.viewingTime.split(":").map(Number);
    const startDateTime = new Date(viewing.viewingDate);
    startDateTime.setHours(hours, minutes, 0, 0);

    const endDateTime = new Date(startDateTime);
    endDateTime.setMinutes(endDateTime.getMinutes() + viewing.duration);

    // Build event description
    const description = buildViewingDescription(viewing);

    const summary = `\u{1F3E0} Property Viewing: ${viewing.propertyTitle}`;
    const location = viewing.propertyAddress;

    // Check for Google Calendar connection first
    const { connected: googleConnected } =
      await hasGoogleCalendarConnection(userId);
    if (googleConnected) {
      const calendars = await listGoogleCalendars(userId);
      if (calendars && calendars.length > 0) {
        const primaryCalendar =
          calendars.find((c) => c.primary) || calendars[0];

        const googleEvent: GoogleEventInput = {
          summary,
          description,
          start: {
            dateTime: startDateTime.toISOString(),
            timeZone,
          },
          end: {
            dateTime: endDateTime.toISOString(),
            timeZone,
          },
          attendees: viewing.clientEmail
            ? [
                {
                  email: viewing.clientEmail,
                  displayName: viewing.clientName,
                },
              ]
            : undefined,
        };

        // For virtual viewings, include the meeting link as conference data
        if (
          viewing.viewingType.toLowerCase() === "virtual" &&
          viewing.meetingLink
        ) {
          googleEvent.description += `\n\nJoin: ${viewing.meetingLink}`;
        }

        const createdEvent = await createGoogleCalendarEvent(
          userId,
          primaryCalendar.id,
          googleEvent,
        );
        if (createdEvent) {
          logger.info("Viewing synced to Google Calendar", {
            viewingId: viewing.viewingId,
            eventId: createdEvent.id,
            calendarId: primaryCalendar.id,
          });

          // Store the Google event ID in booking notes for future sync
          try {
            const noteNow = new Date();
            await bookingNotesAdapter.create({
              data: {
                id: `gcal-viewing-${viewing.viewingId}-${Date.now()}`,
                bookingId: viewing.viewingId,
                content: `google_calendar_event_id:${createdEvent.id}`,
                authorId: userId,
                authorName: "System",
                createdAt: noteNow,
                updatedAt: noteNow,
              },
            });
          } catch (noteError) {
            logger.warn(
              "Failed to store Google event ID for viewing",
              {
                viewingId: viewing.viewingId,
                error:
                  noteError instanceof Error
                    ? noteError.message
                    : "Unknown error",
              },
            );
          }
        }
        return; // Google Calendar sync done, no need for Outlook
      }
    }

    // Check for Outlook Calendar connection if Google not connected
    const { connected: outlookConnected } = await hasOutlookConnection(userId);
    if (outlookConnected) {
      const calendars = await listOutlookCalendars(userId);
      if (calendars && calendars.length > 0) {
        const defaultCalendar =
          calendars.find((c) => c.isDefaultCalendar) || calendars[0];

        const outlookEvent: CreateOutlookEventInput = {
          subject: summary,
          body: {
            contentType: "Text",
            content: description,
          },
          start: {
            dateTime: startDateTime.toISOString(),
            timeZone,
          },
          end: {
            dateTime: endDateTime.toISOString(),
            timeZone,
          },
          location: {
            displayName: location,
          },
          attendees: viewing.clientEmail
            ? [
                {
                  emailAddress: {
                    address: viewing.clientEmail,
                    name: viewing.clientName,
                  },
                  type: "optional",
                },
              ]
            : undefined,
          showAs: "busy",
          isReminderOn: true,
          reminderMinutesBeforeStart: 30,
        };

        const createdEvent = await createOutlookCalendarEvent(
          userId,
          defaultCalendar.id,
          outlookEvent,
        );
        if (createdEvent) {
          logger.info("Viewing synced to Outlook Calendar", {
            viewingId: viewing.viewingId,
            eventId: createdEvent.id,
            calendarId: defaultCalendar.id,
          });

          // Store the Outlook event ID for future sync
          try {
            const outlookNoteNow = new Date();
            await bookingNotesAdapter.create({
              data: {
                id: `outlook-viewing-${viewing.viewingId}-${Date.now()}`,
                bookingId: viewing.viewingId,
                content: `outlook_calendar_event_id:${createdEvent.id}`,
                authorId: userId,
                authorName: "System",
                createdAt: outlookNoteNow,
                updatedAt: outlookNoteNow,
              },
            });
          } catch (noteError) {
            logger.warn(
              "Failed to store Outlook event ID for viewing",
              {
                viewingId: viewing.viewingId,
                error:
                  noteError instanceof Error
                    ? noteError.message
                    : "Unknown error",
              },
            );
          }
        }
      }
    }

    // No calendar integration connected - this is fine, just log it
    logger.debug("No calendar integration connected for user", { userId });
  } catch (error) {
    // Log but don't fail - calendar sync is non-critical
    logger.error("Viewing calendar sync failed (non-blocking)", {
      viewingId: viewing.viewingId,
      userId,
      error: getErrorMessage(error),
    });
  }
}

/**
 * Remove a viewing event from the user's connected calendar.
 *
 * Stub implementation — Google Calendar event deletion requires storing
 * the calendar event ID at creation time, which we do via booking_notes.
 * Full deletion logic can be added later by reading the stored event ID
 * and calling the Google/Outlook delete API.
 *
 * This function is non-blocking — all errors are caught and logged,
 * never thrown.
 */
export async function removeViewingFromCalendar(
  userId: string,
  viewingId: string,
): Promise<void> {
  try {
    logger.info(
      "removeViewingFromCalendar called (stub — deletion not yet implemented)",
      {
        userId,
        viewingId,
      },
    );
  } catch (error) {
    logger.error("removeViewingFromCalendar failed (non-blocking)", {
      viewingId,
      userId,
      error: getErrorMessage(error),
    });
  }
}
