import { baseAPI } from "@/lib/api";
import { redirectToLoginAfterSessionExpired } from "@/features/auth/service/auth-service";
import type { StakeholderListQuery } from "@/features/stakeholder/stakeholder-list-query";
import type {
  ApiResponse,
  Stakeholder,
  StakeholderDashboardSummary,
  StakeholderDetail,
  StakeholderFormPayload,
  StakeholderListMeta,
  StakeholderListResult,
  StakeholderType,
  StakeholderUser,
  StakeholderUserPayload,
} from "@/features/stakeholder/stakeholder-data";

const API_URL = process.env.NEXT_PUBLIC_API_URL ?? "";

// Multipart request helper. We can't reuse `baseAPI` because it forces
// Content-Type: application/json, which would break the multipart boundary.
async function requestMultipart<T>(
  path: string,
  method: "POST" | "PATCH",
  formData: FormData,
): Promise<T> {
  const response = await fetch(`${API_URL}${path}`, {
    method,
    credentials: "include",
    body: formData,
  });

  if (!response.ok) {
    if (response.status === 401) {
      redirectToLoginAfterSessionExpired();
    }

    throw new Error(`API Error: ${response.status}`);
  }

  return (await response.json()) as T;
}

// Build a multipart body for create/update when a logo file was picked.
function buildStakeholderFormData(payload: StakeholderFormPayload): FormData {
  const formData = new FormData();

  formData.append("name", payload.name);
  formData.append("stakeholderTypeId", String(payload.stakeholderTypeId));
  formData.append("active", String(payload.active));

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

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

  return formData;
}

// Strip the file before falling back to a JSON request.
function toJsonPayload(payload: StakeholderFormPayload) {
  const { logoFile: _logoFile, ...rest } = payload;
  return rest;
}

type StakeholderData =
  | Stakeholder
  | { stakeholder?: Stakeholder | null }
  | null;
type StakeholderDetailData =
  | StakeholderDetail
  | { stakeholder?: StakeholderDetail | null }
  | null;
type UserCollection =
  | StakeholderUser[]
  | { users?: StakeholderUser[]; items?: StakeholderUser[] }
  | null;

type StakeholderMutationResult = {
  message: string;
  stakeholder: Stakeholder | null;
};

function extractStakeholder(data: StakeholderData): Stakeholder | null {
  if (!data) {
    return null;
  }

  if ("stakeholder" in data) {
    return data.stakeholder ?? null;
  }

  return data as Stakeholder;
}

function extractStakeholderDetail(
  data: StakeholderDetailData,
): StakeholderDetail | null {
  if (!data) {
    return null;
  }

  if ("stakeholder" in data) {
    const stakeholder = data.stakeholder;

    return stakeholder
      ? {
          ...stakeholder,
          users: Array.isArray(stakeholder.users) ? stakeholder.users : [],
        }
      : null;
  }

  const stakeholder = data as StakeholderDetail;

  return {
    ...stakeholder,
    users: Array.isArray(stakeholder.users) ? stakeholder.users : [],
  };
}

function extractUsers(data: UserCollection): StakeholderUser[] {
  if (Array.isArray(data)) {
    return data;
  }

  if (data?.users && Array.isArray(data.users)) {
    return data.users;
  }

  if (data?.items && Array.isArray(data.items)) {
    return data.items;
  }

  return [];
}

const EMPTY_LIST_META: StakeholderListMeta = {
  page: 1,
  limit: 10,
  total: 0,
  totalPages: 0,
};

export async function getStakeholders(
  params: StakeholderListQuery,
): Promise<StakeholderListResult> {
  const query = new URLSearchParams({
    page: String(params.page),
    limit: String(params.limit),
  });

  if (params.sortBy) {
    query.set("sortBy", params.sortBy);
  }

  if (params.sortOrder) {
    query.set("sortOrder", params.sortOrder);
  }

  if (params.search) {
    query.set("search", params.search.slice(0, 200));
  }

  if (params.id !== undefined) {
    query.set("id", String(params.id));
  }

  if (params.stakeholderTypeId !== undefined) {
    query.set("stakeholderTypeId", String(params.stakeholderTypeId));
  }

  if (params.active !== undefined) {
    query.set("active", String(params.active));
  }

  if (params.name) {
    query.set("name", params.name.slice(0, 200));
  }

  if (params.coChair) {
    query.set("coChair", params.coChair.slice(0, 200));
  }

  if (params.description) {
    query.set("description", params.description.slice(0, 200));
  }

  if (params.stakeholderTypeName) {
    query.set("stakeholderTypeName", params.stakeholderTypeName.slice(0, 100));
  }

  params.stakeholderTypeIds?.forEach((typeId) => {
    query.append("stakeholderTypeIds", String(typeId));
  });

  params.years?.forEach((year) => {
    query.append("years", String(year));
  });

  if (params.hasUsers !== undefined) {
    query.set("hasUsers", String(params.hasUsers));
  }

  const response = (await baseAPI(`/stakeholders?${query.toString()}`, {
    method: "GET",
  })) as ApiResponse<Stakeholder[]>;

  return {
    data: Array.isArray(response.data) ? response.data : [],
    meta: response.meta ?? {
      ...EMPTY_LIST_META,
      page: params.page,
      limit: params.limit,
    },
  };
}

export async function getStakeholderDashboardSummary(): Promise<StakeholderDashboardSummary> {
  const response = (await baseAPI("/stakeholders/summary", {
    method: "GET",
  })) as ApiResponse<StakeholderDashboardSummary>;

  return (
    response.data ?? {
      totalStakeholders: 0,
      activeStakeholders: 0,
      inactiveStakeholders: 0,
      totalStakeholderTypes: 0,
      totalAssignedUsers: 0,
      stakeholderTypes: [],
    }
  );
}

export async function getStakeholderTypes(): Promise<StakeholderType[]> {
  const response = (await baseAPI("/stakeholders/types", {
    method: "GET",
  })) as ApiResponse<StakeholderType[]>;

  return Array.isArray(response.data) ? response.data : [];
}

export async function getStakeholder(
  id: number,
): Promise<StakeholderDetail | null> {
  const response = (await baseAPI(`/stakeholders/${id}`, {
    method: "GET",
  })) as ApiResponse<StakeholderDetailData>;

  return extractStakeholderDetail(response.data);
}

export async function createStakeholder(
  payload: StakeholderFormPayload,
): Promise<StakeholderMutationResult> {
  const response = payload.logoFile
    ? await requestMultipart<ApiResponse<StakeholderData>>(
        "/stakeholders",
        "POST",
        buildStakeholderFormData(payload),
      )
    : ((await baseAPI("/stakeholders", {
        method: "POST",
        body: JSON.stringify(toJsonPayload(payload)),
      })) as ApiResponse<StakeholderData>);

  return {
    message: response.message,
    stakeholder: extractStakeholder(response.data),
  };
}

export async function updateStakeholder(
  id: number,
  payload: StakeholderFormPayload,
): Promise<StakeholderMutationResult> {
  const response = payload.logoFile
    ? await requestMultipart<ApiResponse<StakeholderData>>(
        `/stakeholders/${id}`,
        "PATCH",
        buildStakeholderFormData(payload),
      )
    : ((await baseAPI(`/stakeholders/${id}`, {
        method: "PATCH",
        body: JSON.stringify(toJsonPayload(payload)),
      })) as ApiResponse<StakeholderData>);

  return {
    message: response.message,
    stakeholder: extractStakeholder(response.data),
  };
}

export async function deleteStakeholder(id: number): Promise<string> {
  const response = (await baseAPI(`/stakeholders/${id}`, {
    method: "DELETE",
  })) as ApiResponse<null>;

  return response.message;
}

export async function getStakeholderUsers(
  stakeholderId: number,
): Promise<StakeholderUser[]> {
  const response = (await baseAPI(`/stakeholders/${stakeholderId}/users`, {
    method: "GET",
  })) as ApiResponse<UserCollection>;

  return extractUsers(response.data);
}

export async function getStakeholderUserOptions(): Promise<StakeholderUser[]> {
  const response = (await baseAPI("/users", {
    method: "GET",
  })) as ApiResponse<UserCollection>;

  return extractUsers(response.data);
}

export async function addUserToStakeholder(
  stakeholderId: number,
  payload: StakeholderUserPayload,
): Promise<string> {
  const response = (await baseAPI(`/stakeholders/${stakeholderId}/users`, {
    method: "POST",
    body: JSON.stringify(payload),
  })) as ApiResponse<unknown>;

  return response.message || "User added to stakeholder.";
}
