import type { MeetingCalendarStatus } from "./meeting-calendar-data";
import type { MeetingCalendarApiMeeting } from "./meeting-calendar-data";

export type MeetingDocument = {
  name: string;
  type: string;
  size: number;
};

export type MeetingGuest = {
  id: string;
  name: string;
  email: string;
  avatar?: string | null;
  type: "user" | "email";
};

export type MeetingCreateForm = {
  title: string;
  description: string;
  status?: string;
  date: string;
  startTime: string;
  endTime: string;
  location: string;
  guests: string;
  guestEntries: MeetingGuest[];
  document: MeetingDocument | null;
  meetingRequestId?: number;
};

export type MeetingCreateFormErrors = Partial<
  Record<keyof MeetingCreateForm, string>
>;

export type MeetingCalendarRowOptions = {
  status?: MeetingCalendarStatus;
  workingGroup?: string;
  issueCount?: number;
  meetingRequest?: string;
};

export type MeetingSavePayload = {
  title: string;
  description?: string;
  meetingDate?: string;
  startTime?: string;
  endTime?: string;
  location?: string;
  documentReference?: string;
  status: MeetingCalendarStatus;
  meetingRequestId?: number;
  userId: number;
  guestUserIds?: number[];
  guestEmails?: string[];
  sendInvites?: boolean;
};

export type MeetingSavePayloadOptions = {
  status: MeetingCalendarStatus;
  userId: number;
  meetingRequestId?: number;
  fallbackTitle?: string;
  documentReference?: string;
  sendInvites?: boolean;
};

export type MeetingTimeParts = {
  hour: number;
  minute: number;
  period: "AM" | "PM";
};

export const emptyMeetingCreateForm: MeetingCreateForm = {
  title: "",
  description: "",
  date: "",
  startTime: "",
  endTime: "",
  location: "",
  guests: "",
  guestEntries: [],
  document: null,
};

const MAX_PDF_SIZE = 10 * 1024 * 1024;

export function dateToMeetingFormValue(date: Date | null) {
  if (!date) return "";

  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");

  return `${year}-${month}-${day}`;
}

export function meetingFormValueToDate(value: string) {
  if (!value) return null;

  const [year, month, day] = value.split("-").map(Number);
  const date = new Date(year, month - 1, day);

  return Number.isNaN(date.getTime()) ? null : date;
}

export function meetingTimeToParts(value: string): MeetingTimeParts {
  if (!value) {
    return { hour: 12, minute: 0, period: "PM" };
  }

  const [hours, minutes] = value.split(":").map(Number);
  const period = hours >= 12 ? "PM" : "AM";
  const hour = hours % 12 || 12;

  return { hour, minute: minutes, period };
}

export function timePartsToMeetingValue(parts: MeetingTimeParts) {
  const periodHours = parts.hour % 12;
  const hours = parts.period === "PM" ? periodHours + 12 : periodHours;

  return `${String(hours).padStart(2, "0")}:${String(parts.minute).padStart(2, "0")}`;
}

export function adjustMeetingTime(
  parts: MeetingTimeParts,
  section: "hour" | "minute",
  amount: number,
): MeetingTimeParts {
  if (section === "hour") {
    const hour = ((parts.hour - 1 + amount + 12) % 12) + 1;
    return { ...parts, hour };
  }

  const minute = (parts.minute + amount + 60) % 60;
  return { ...parts, minute };
}

export function validateMeetingForm(
  form: MeetingCreateForm,
): MeetingCreateFormErrors {
  const errors: MeetingCreateFormErrors = {};

  if (!form.title.trim()) errors.title = "Meeting title is required.";
  if (!form.date) errors.date = "Date is required.";
  if (!form.startTime) errors.startTime = "Start time is required.";
  if (!form.endTime) errors.endTime = "End time is required.";
  if (!form.location.trim()) errors.location = "Location is required.";
  if (!form.guests.trim() && (form.guestEntries ?? []).length === 0) {
    errors.guests = "At least one guest is required.";
  }

  if (!form.document) {
    errors.document = "Document reference is required.";
  } else if (form.document.type !== "application/pdf") {
    errors.document = "Only PDF files are allowed.";
  } else if (form.document.size > MAX_PDF_SIZE) {
    errors.document = "PDF file must be 10 MB or smaller.";
  }

  if (
    form.startTime &&
    form.endTime &&
    form.endTime <= form.startTime
  ) {
    errors.endTime = "End time must be after start time.";
  }

  return errors;
}

export function formatMeetingDate(value: string) {
  if (!value) return "-";

  const [year, month, day] = value.split("-").map(Number);
  if (!year || !month || !day) return "-";

  const date = new Date(Date.UTC(year, month - 1, day));
  if (Number.isNaN(date.getTime())) return "-";

  return new Intl.DateTimeFormat("en-US", {
    month: "long",
    day: "numeric",
    year: "numeric",
    timeZone: "UTC",
  }).format(date);
}

function formatMeetingTime(value: string) {
  if (!value) return "";

  const [hours, minutes] = value.split(":").map(Number);
  if (!Number.isInteger(hours) || !Number.isInteger(minutes)) return "";

  const date = new Date(Date.UTC(2000, 0, 1, hours, minutes));
  if (Number.isNaN(date.getTime())) return "";

  return new Intl.DateTimeFormat("en-US", {
    hour: "numeric",
    minute: "2-digit",
    hour12: true,
    timeZone: "UTC",
  }).format(date);
}

export function formatMeetingTimeRange(startTime: string, endTime: string) {
  const start = formatMeetingTime(startTime);
  const end = formatMeetingTime(endTime);

  if (!start || !end) return "-";

  return `${start} - ${end}`;
}

function trimOrUndefined(value?: string | null) {
  const trimmed = value?.trim();

  return trimmed ? trimmed : undefined;
}

function uniqueNumbers(values: number[]) {
  return Array.from(new Set(values));
}

function uniqueStrings(values: string[]) {
  return Array.from(new Set(values));
}

function getGuestUserIds(entries: MeetingGuest[]) {
  return uniqueNumbers(
    entries
      .filter((guest) => guest.type === "user")
      .map((guest) => Number(guest.id))
      .filter((id) => Number.isFinite(id) && id > 0),
  );
}

function getGuestEmails(form: MeetingCreateForm) {
  const selectedEmails = form.guestEntries
    .filter((guest) => guest.type === "email")
    .map((guest) => guest.email.trim())
    .filter(Boolean);

  const typedEmails = form.guests
    .split(",")
    .map((guest) => guest.trim())
    .filter(Boolean);

  return uniqueStrings([...selectedEmails, ...typedEmails]);
}

export function createMeetingSavePayload(
  form: MeetingCreateForm,
  options: MeetingSavePayloadOptions,
): MeetingSavePayload {
  const title =
    trimOrUndefined(form.title) ||
    trimOrUndefined(options.fallbackTitle) ||
    "Untitled meeting draft";
  const description = trimOrUndefined(form.description);
  const location = trimOrUndefined(form.location);
  const guestUserIds = getGuestUserIds(form.guestEntries ?? []);
  const guestEmails = getGuestEmails(form);
  const documentReference =
    form.document?.name || trimOrUndefined(options.documentReference);

  return {
    title,
    ...(description ? { description } : {}),
    ...(form.date && { meetingDate: form.date }),
    ...(form.startTime && { startTime: form.startTime }),
    ...(form.endTime && { endTime: form.endTime }),
    ...(location ? { location } : {}),
    ...(documentReference && { documentReference }),
    status: options.status,
    ...((options.meetingRequestId ?? form.meetingRequestId) && {
      meetingRequestId: options.meetingRequestId ?? form.meetingRequestId,
    }),
    userId: options.userId,
    ...(options.sendInvites !== undefined && {
      sendInvites: options.sendInvites,
    }),
    ...(guestUserIds.length > 0 && { guestUserIds }),
    ...(guestEmails.length > 0 && { guestEmails }),
  };
}

function getDocumentNameFromReference(value?: string | null) {
  if (!value) return null;

  const trimmed = value.trim();
  if (!trimmed) return null;

  const segments = trimmed.split("/");
  return segments[segments.length - 1] || trimmed;
}

function apiDateToFormValue(value?: string | null) {
  if (!value) return "";

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

  const year = date.getUTCFullYear();
  const month = String(date.getUTCMonth() + 1).padStart(2, "0");
  const day = String(date.getUTCDate()).padStart(2, "0");

  return `${year}-${month}-${day}`;
}

function apiTimeToFormValue(value?: string | null) {
  if (!value) return "";

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

  return `${String(date.getUTCHours()).padStart(2, "0")}:${String(date.getUTCMinutes()).padStart(2, "0")}`;
}

export function mapApiMeetingToCreateForm(
  meeting: MeetingCalendarApiMeeting,
): Partial<MeetingCreateForm> {
  const guestEntries: MeetingGuest[] = (meeting.guests ?? []).flatMap(
    (guest): MeetingGuest[] => {
      if (guest.user?.id && guest.user.email) {
        return [
          {
            id: String(guest.user.id),
            name: guest.user.name?.trim() || guest.user.email,
            email: guest.user.email,
            avatar: guest.user.avatar,
            type: "user",
          },
        ];
      }

      if (guest.email) {
        return [
          {
            id: guest.email,
            name: guest.email,
            email: guest.email,
            type: "email",
          },
        ];
      }

      return [];
    },
  );

  const guestEmails = guestEntries
    .filter((guest) => guest.type === "email")
    .map((guest) => guest.email);

  const documentName = getDocumentNameFromReference(meeting.documentReference);

  return {
    title: meeting.title?.trim() || "",
    description: meeting.description?.trim() || "",
    status: meeting.status?.trim() || undefined,
    date: apiDateToFormValue(meeting.meetingDate),
    startTime: apiTimeToFormValue(meeting.startTime),
    endTime: apiTimeToFormValue(meeting.endTime),
    location: meeting.location?.trim() || "",
    guests: guestEmails.join(", "),
    guestEntries,
    document: documentName
      ? {
          name: documentName,
          type: "application/pdf",
          size: 0,
        }
      : null,
  };
}
