import type { MeetingCalendarStatus } from "./meeting-calendar-data";
import type { MeetingCalendarApiMeeting } from "./meeting-calendar-data";
import {
  getDisplayFileName,
  type UploadedFileMetadata,
} from "@/lib/document-file";

export type MeetingDocument = {
  path?: string;
  name: string;
  type: string;
  size: number;
  file?: File;
};

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

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;
  documentFile?: File;
  status: MeetingCalendarStatus;
  meetingRequestId?: number;
  userId: number;
  guestUserIds?: number[];
  sendInvites?: boolean;
};

export type MeetingSavePayloadOptions = {
  status: MeetingCalendarStatus;
  userId: number;
  meetingRequestId?: number;
  fallbackTitle?: string;
  /** Existing files are kept by the API; this value is not sent in FormData. */
  documentReference?: string;
  sendInvites?: boolean;
};

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 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 getGuestUserIds(entries: MeetingGuest[]) {
  return uniqueNumbers(
    entries
      .filter((guest) => guest.type === "user")
      .map((guest) => Number(guest.id))
      .filter((id) => Number.isFinite(id) && id > 0),
  );
}

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 ?? []);
  return {
    title,
    ...(description ? { description } : {}),
    ...(form.date && { meetingDate: form.date }),
    ...(form.startTime && { startTime: form.startTime }),
    ...(form.endTime && { endTime: form.endTime }),
    ...(location ? { location } : {}),
    ...(form.document?.file && { documentFile: form.document.file }),
    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 }),
  };
}

function getDocumentMetadata(
  value?: UploadedFileMetadata | string | null,
): UploadedFileMetadata | null {
  if (!value) return null;

  if (typeof value === "string") {
    const name = getDisplayFileName(value, "");
    return name
      ? {
          path: value,
          name,
          size: 0,
          mimeType: "application/pdf",
        }
      : null;
  }

  return value;
}

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",
          },
        ];
      }

      return [];
    },
  );

  const uniqueGuestEntries = guestEntries.filter(
    (guest, index, entries) =>
      entries.findIndex((entry) => entry.id === guest.id) === index,
  );

  const document = getDocumentMetadata(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: uniqueGuestEntries.map((guest) => guest.email).join(", "),
    guestEntries: uniqueGuestEntries,
    document: document
      ? {
          path: document.path,
          name: document.name,
          type: document.mimeType,
          size: document.size,
        }
      : null,
  };
}
