"use client";

import type {
  DecisionIssueItem,
  MinistryRgcDecisionRow,
  MinistryRgcDecisionStatus,
} from "../data/ministry-rgc-decision-data";

const API_BASE_URL =
  process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "") ??
  "http://localhost:3001/api/v1";

type ApiUser = {
  id?: number | string;
  userId?: number | string;
  sub?: number | string;
};

type AuthMeResponse = {
  message?: string | string[];
  data?: ApiUser | { user?: ApiUser };
  user?: ApiUser;
};

type ApiStakeholder = {
  id?: number | string;
  name?: string | null;
  logo?: string | null;
};

type ApiCategory = {
  id?: number | string;
  name?: string | null;
};

type ApiIndicator = {
  id?: number | string;
  name?: string | null;
  description?: string | null;
};

type ApiIssueItem = {
  id?: number | string;
  title?: string | null;
  name?: string | null;
  description?: string | null;
  recommendation?: string | null;
};

type ApiProgressUpdate = {
  indicators?: unknown;
  progressSolution?: unknown;
  implementationChallenges?: unknown;
  requests?: unknown;
  nextStep?: unknown;
  dateOfIssueSolution?: string | null;
  attachment?: string | null;
};

type ApiPlenary = {
  id?: number | string;
  name?: string | null;
  title?: string | null;
};

type ApiDecision = {
  id?: number | string;

  plenaryId?: number | string | null;
  plenary?: ApiPlenary | null;
  plenaryName?: string | null;

  stakeholderId?: number | string | null;
  stakeholder?: ApiStakeholder | null;

  categoryId?: number | string | null;
  category?: string | ApiCategory | null;
  categoryInfo?: ApiCategory | null;

  indicatorId?: number | string | null;
  indicator?: ApiIndicator | null;
  indicatorName?: string | null;

  meetingDate?: string | null;
  status?: string | null;
  statusCode?: string | null;

  focalPerson?: string | null;
  decision?: string | null;

  verificationSource?: string | null;
  sourceOfVerification?: string | null;
  verificationLink?: string | null;

  issues?: ApiIssueItem[];
  progressUpdate?: ApiProgressUpdate | null;
};

type ApiListData<T> = {
  items?: T[];
  data?: T[];
};

type ApiResponse<T> = {
  message?: string | string[];
  data?: T;
  items?: ApiDecision[];
};

function toPositiveNumber(value: unknown): number | null {
  const numberValue = Number(value);

  return Number.isInteger(numberValue) && numberValue > 0 ? numberValue : null;
}

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

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

function getMessage(
  message: string | string[] | undefined,
  fallback: string,
): string {
  return Array.isArray(message)
    ? message.join(", ")
    : message?.trim() || fallback;
}

function readUserId(value: unknown): string | null {
  if (!value || typeof value !== "object") {
    return null;
  }

  const record = value as Record<string, unknown>;

  const id = toPositiveNumber(record.id ?? record.userId ?? record.sub);

  return id ? String(id) : null;
}

async function readResponseJson<T>(response: Response): Promise<T> {
  try {
    return (await response.json()) as T;
  } catch {
    return {} as T;
  }
}

async function getCurrentUserId(): Promise<string> {
  const token = getAccessToken();

  const response = await fetch(`${API_BASE_URL}/auth/me`, {
    method: "GET",
    credentials: "include",
    cache: "no-store",
    headers: {
      Accept: "application/json",
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
    },
  });

  const payload = await readResponseJson<AuthMeResponse>(response);

  if (!response.ok) {
    throw new Error(
      getMessage(
        payload.message,
        "Your login session has expired. Please login again.",
      ),
    );
  }

  const nestedUser =
    payload.data && typeof payload.data === "object" && "user" in payload.data
      ? payload.data.user
      : null;

  const userId =
    readUserId(payload.data) ??
    readUserId(nestedUser) ??
    readUserId(payload.user);

  if (!userId) {
    throw new Error("Authenticated user ID was not returned by /auth/me.");
  }

  return userId;
}

function mapStatus(value?: string | null): MinistryRgcDecisionStatus {
  const normalized = String(value ?? "")
    .trim()
    .toUpperCase()
    .replaceAll("_", " ")
    .replaceAll("-", " ");

  if (normalized === "SOLVED") {
    return "Solved";
  }

  if (normalized === "IN PROGRESS") {
    return "In Progress";
  }

  return "Not Addressed";
}

function extractItems(
  payload: ApiResponse<ApiListData<ApiDecision> | ApiDecision[]>,
): ApiDecision[] {
  if (Array.isArray(payload.data)) {
    return payload.data;
  }

  if (payload.data && typeof payload.data === "object") {
    if (Array.isArray(payload.data.items)) {
      return payload.data.items;
    }

    if (Array.isArray(payload.data.data)) {
      return payload.data.data;
    }
  }

  return Array.isArray(payload.items) ? payload.items : [];
}

function getCategoryName(item: ApiDecision): string {
  if (item.categoryInfo?.name?.trim()) {
    return item.categoryInfo.name.trim();
  }

  if (
    item.category &&
    typeof item.category === "object" &&
    item.category.name?.trim()
  ) {
    return item.category.name.trim();
  }

  return typeof item.category === "string" ? item.category.trim() : "";
}

function getCategoryId(item: ApiDecision): number | null {
  return (
    toPositiveNumber(item.categoryId) ??
    toPositiveNumber(item.categoryInfo?.id) ??
    (typeof item.category === "object"
      ? toPositiveNumber(item.category?.id)
      : null)
  );
}

function getPlenaryName(item: ApiDecision): string | null {
  const name =
    item.plenary?.name?.trim() ||
    item.plenary?.title?.trim() ||
    item.plenaryName?.trim() ||
    "";

  return name || null;
}

function mapIssues(items?: ApiIssueItem[]): DecisionIssueItem[] {
  if (!Array.isArray(items)) {
    return [];
  }

  return items
    .map((issue, index) => {
      const issueId = toPositiveNumber(issue.id) ?? 0;

      return {
        id: issueId,
        title:
          issue.title?.trim() ||
          issue.name?.trim() ||
          `Issue ${issueId || index + 1}`,
        name: issue.name?.trim() || undefined,
        description: issue.description?.trim() || null,
        recommendation: issue.recommendation?.trim() || null,
      };
    })
    .filter(
      (issue) =>
        issue.id > 0 ||
        Boolean(issue.title?.trim()) ||
        Boolean(issue.name?.trim()),
    );
}

function getProgressText(value: unknown): string {
  if (typeof value === "string") {
    return value.trim();
  }

  if (value === null || value === undefined) {
    return "";
  }

  return JSON.stringify(value);
}

function resolveProgressAttachment(value?: string | null): string | null {
  const path = value?.trim();

  if (!path) return null;
  if (/^https?:\/\//i.test(path)) return path;

  try {
    const normalizedPath = path.startsWith("/") ? path : `/${path}`;
    return `${new URL(API_BASE_URL).origin}${normalizedPath}`;
  } catch {
    return path;
  }
}

function joinIssueField(
  issues: DecisionIssueItem[],
  field: "description" | "recommendation",
): string {
  return Array.from(
    new Set(
      issues
        .map((issue) => issue[field]?.trim())
        .filter((value): value is string => Boolean(value)),
    ),
  ).join("\n");
}

export async function getMinistryRgcDecisions(): Promise<
  MinistryRgcDecisionRow[]
> {
  const token = getAccessToken();
  const userId = await getCurrentUserId();

  const response = await fetch(
    `${API_BASE_URL}/rgc-decisions?limit=100&ministryOnly=true`,
    {
      method: "GET",
      credentials: "include",
      cache: "no-store",
      headers: {
        Accept: "application/json",
        "x-user-id": userId,
        ...(token
          ? {
              Authorization: `Bearer ${token}`,
            }
          : {}),
      },
    },
  );

  const payload =
    await readResponseJson<
      ApiResponse<ApiListData<ApiDecision> | ApiDecision[]>
    >(response);

  if (!response.ok) {
    throw new Error(
      getMessage(payload.message, "Unable to load Ministry RGC Decisions."),
    );
  }

  return extractItems(payload)
    .map((item): MinistryRgcDecisionRow | null => {
      const id = toPositiveNumber(item.id);

      if (!id) {
        return null;
      }

      const issues = mapIssues(item.issues);
      const progressUpdate = item.progressUpdate;

      return {
        id,

        plenaryId:
          toPositiveNumber(item.plenaryId) ??
          toPositiveNumber(item.plenary?.id),

        plenaryName: getPlenaryName(item),

        ministryId:
          toPositiveNumber(item.stakeholderId) ??
          toPositiveNumber(item.stakeholder?.id),

        ministry: item.stakeholder?.name?.trim() || "",
        ministryLogo: item.stakeholder?.logo?.trim() || null,

        decision: item.decision?.trim() || "",
        meetingDate: item.meetingDate?.slice(0, 10) || "",

        categoryId: getCategoryId(item),
        category: getCategoryName(item),

        status: mapStatus(item.statusCode ?? item.status),

        indicator:
          item.indicator?.name?.trim() || item.indicatorName?.trim() || "",

        focalPerson: item.focalPerson?.trim() || "",

        sourceOfVerification:
          item.verificationSource?.trim() ||
          item.sourceOfVerification?.trim() ||
          "",

        verificationLink: item.verificationLink?.trim() || null,

        issues,
        issueDescription: joinIssueField(issues, "description"),
        recommendations: joinIssueField(issues, "recommendation"),
        indicators: getProgressText(progressUpdate?.indicators),
        progressSolution: getProgressText(progressUpdate?.progressSolution),
        implementationChallenges: getProgressText(
          progressUpdate?.implementationChallenges,
        ),
        request: getProgressText(progressUpdate?.requests),
        nextStep: getProgressText(progressUpdate?.nextStep),
        dateOfIssueSolution: progressUpdate?.dateOfIssueSolution || null,
        progressAttachment: resolveProgressAttachment(
          progressUpdate?.attachment,
        ),
      };
    })
    .filter((item): item is MinistryRgcDecisionRow => item !== null);
}

export default getMinistryRgcDecisions;
