import {
  mapApiMeetingToCalendarRow,
  type MeetingCalendarApiMeeting,
  type MeetingCalendarApiListResponse,
  type MeetingCalendarRow,
} from "./meeting-calendar-data";
import type { MeetingSavePayload } from "./meeting-create-form";

const API_BASE_URL =
  process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001/api/v1";

type ApiResponse<T> = {
  success: boolean;
  statusCode: number;
  message: string;
  data: T;
};

export type MeetingGuestUser = {
  id: number;
  name?: string | null;
  email?: string | null;
  avatar?: string | null;
  position?: string | null;
};

function getAccessToken() {
  if (typeof window === "undefined") return "";

  return (
    localStorage.getItem("accessToken") ||
    localStorage.getItem("token") ||
    localStorage.getItem("authToken") ||
    ""
  );
}

async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
  const token = getAccessToken();
  const hasBody = Boolean(options.body);
  const isFormData =
    typeof FormData !== "undefined" && options.body instanceof FormData;

  const response = await fetch(`${API_BASE_URL}${path}`, {
    ...options,
    cache: "no-store",
    credentials: "include",
    headers: {
      Accept: "application/json",
      ...(hasBody && !isFormData ? { "Content-Type": "application/json" } : {}),
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...options.headers,
    },
  });

  const json = (await response
    .json()
    .catch(() => null)) as ApiResponse<T> | null;

  if (!response.ok) {
    throw new Error(json?.message || `API error ${response.status}`);
  }

  return json?.data as T;
}

function createMeetingFormData(payload: MeetingSavePayload) {
  const formData = new FormData();

  formData.append("title", payload.title);
  if (payload.description !== undefined) {
    formData.append("description", payload.description);
  }
  if (payload.meetingDate !== undefined) {
    formData.append("meetingDate", payload.meetingDate);
  }
  if (payload.startTime !== undefined) {
    formData.append("startTime", payload.startTime);
  }
  if (payload.endTime !== undefined) {
    formData.append("endTime", payload.endTime);
  }
  if (payload.location !== undefined) {
    formData.append("location", payload.location);
  }
  if (payload.status !== undefined) {
    formData.append("status", payload.status);
  }
  if (payload.meetingRequestId !== undefined) {
    formData.append("meetingRequestId", String(payload.meetingRequestId));
  }
  formData.append("userId", String(payload.userId));
  if (payload.guestUserIds?.length) {
    formData.append("guestUserIds", payload.guestUserIds.join(","));
  }
  if (payload.sendInvites !== undefined) {
    formData.append("sendInvites", String(payload.sendInvites));
  }
  if (payload.documentFile) {
    formData.append("documentReference", payload.documentFile);
  }

  return formData;
}

export async function getMeetingCalendarRows(): Promise<MeetingCalendarRow[]> {
  const data = await apiFetch<MeetingCalendarApiListResponse>("/meetings");

  return data.items.map(mapApiMeetingToCalendarRow);
}

export async function getMeetingById(
  id: number,
): Promise<MeetingCalendarApiMeeting> {
  return apiFetch<MeetingCalendarApiMeeting>(`/meetings/${id}`);
}

export async function createMeeting(
  payload: MeetingSavePayload,
): Promise<MeetingCalendarApiMeeting> {
  const data = await apiFetch<{
    message: string;
    meeting: MeetingCalendarApiMeeting;
  }>("/meetings", {
    method: "POST",
    body: createMeetingFormData(payload),
  });

  return data.meeting;
}

export async function updateMeeting(
  id: number,
  payload: MeetingSavePayload,
): Promise<MeetingCalendarApiMeeting> {
  const data = await apiFetch<{
    message: string;
    meeting: MeetingCalendarApiMeeting;
  }>(`/meetings/${id}`, {
    method: "PATCH",
    body: createMeetingFormData(payload),
  });

  return data.meeting;
}

export async function getMeetingRequestGuestUsers(
  meetingRequestId: number,
): Promise<MeetingGuestUser[]> {
  return apiFetch<MeetingGuestUser[]>(
    `/meetings/meeting-requests/${meetingRequestId}/guest-users`,
  );
}

// --- Adding an issue to a meeting ------------------------------------------
// The Ministry adds the issue to the PSWG meeting request the meeting is
// linked to, which is where the meeting's other issues come from too.

// One option in the category / agency dropdowns of the Add Issue form.
export type IssueFormOption = {
  id: number;
  name: string;
  logo?: string | null;
};

export type AddMeetingIssuePayload = {
  title: string;
  description: string;
  recommendation: string;
  categoryId?: number;
  // The private-sector working group that owns the issue.
  workingGroupId?: number;
  // Second to fifth responsible agencies, in the order they were picked.
  additionalAgencyIds?: number[];
  attachmentFile?: File | null;
};

export async function getIssueCategories(): Promise<IssueFormOption[]> {
  return apiFetch<IssueFormOption[]>("/working-group-issues/categories");
}

// Active private-sector working groups, for the "Working Group Name" select.
export async function getWorkingGroups(): Promise<IssueFormOption[]> {
  return apiFetch<IssueFormOption[]>("/stakeholders/working-groups");
}

export async function getIssueGovernmentAgencies(): Promise<IssueFormOption[]> {
  return apiFetch<IssueFormOption[]>(
    "/working-group-issues/government-agencies",
  );
}

export async function createMeetingRequestIssue(
  meetingRequestId: number,
  payload: AddMeetingIssuePayload,
): Promise<{ message: string }> {
  const formData = new FormData();

  formData.append("title", payload.title);
  formData.append("description", payload.description);
  formData.append("recommendation", payload.recommendation);

  if (payload.categoryId !== undefined) {
    formData.append("categoryId", String(payload.categoryId));
  }

  if (payload.workingGroupId !== undefined) {
    formData.append("workingGroupId", String(payload.workingGroupId));
  }

  // The backend reads this list as JSON, the same way it reads the agencies
  // of the other issue create endpoints.
  if (payload.additionalAgencyIds?.length) {
    formData.append(
      "additionalAgencyIds",
      JSON.stringify(payload.additionalAgencyIds),
    );
  }

  if (payload.attachmentFile) {
    formData.append(
      "attachmentFile",
      payload.attachmentFile,
      payload.attachmentFile.name,
    );
  }

  return apiFetch<{ message: string }>(
    `/meeting-requests/${meetingRequestId}/issues`,
    { method: "POST", body: formData },
  );
}
