import type { PlenaryRecord, RgcDecision, DecisionIssueItem } from "../../plenary-data";

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

const DISPLAY_TIME_ZONE = "Asia/Phnom_Penh";

type ApiStatus = string | null | undefined;

type ApiPlenaryItem = {
  id: number;
  name: string;
  meetingDate: string | null;
  deadline: string | null;
  documentReference: string | null;
  status: ApiStatus;
  statusCode?: string | null;
  numberOfRgcDecisions?: number | null;
};

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

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

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

type ApiRgcDecisionItem = {
  id: number;
  plenaryId?: number | null;
  stakeholderId?: number | null;
  categoryId?: number | null;
  category?: string | ApiCategory | null;
  categoryInfo?: ApiCategory | null;
  indicatorId?: number | null;
  indicator?: ApiIndicator | string | null;
  indicatorName?: string | null;
  meetingDate?: string | null;
  status?: string | null;
  statusCode?: string | null;
  focalPerson?: string | null;
  decision?: string | null;
  verificationSource?: string | null;
  verificationLink?: string | null;
  linkToVerificationSource?: string | null;
  verificationSourceLink?: string | null;
  issues?: ApiIssueItem[];
};

type AuthUser = {
  id?: number | string;
  userId?: number | string;
  name?: string | null;
  email?: string | null;
  position?: string | null;
};

type AuthMeResponse = {
  id?: number | string;
  user?: AuthUser;
  data?:
    | (AuthUser & {
        user?: AuthUser;
      })
    | null;
  message?: string | string[];
};

type ApiPaginationMeta = {
  total?: number;
  page?: number;
  limit?: number;
  totalPages?: number;
};

type ApiListData<T> = {
  items?: T[];
  meta?: ApiPaginationMeta;
  total?: number;
};

type ApiListResponse<T> = {
  data?: T[] | ApiListData<T>;
  items?: T[];
  meta?: ApiPaginationMeta;
  total?: number;
  message?: string | string[];
};

type ApiDetailResponse<T> = {
  data?: T;
  message?: string | string[];
} & Partial<T>;

export type RgcDecisionCategory = {
  id: number;
  name: string;
};

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

export type CreateMinistryRgcDecisionInput = {
  plenaryId: number;
  categoryId: number;
  indicatorId: number;
  status: string;
  meetingDate: string;
  focalPerson: string;
  decision: string;
  verificationSource: string;
  verificationLink?: string;
  issueIds?: number[];
};

export type SubmitRgcDecisionNotificationResponse = {
  success?: boolean;
  message?: string;
  data?: ApiRgcDecisionItem;
  notifications?: {
    receiverStakeholderIds?: number[];
    receiverUserIds?: number[];
    created?: number;
    updated?: number;
    skipped?: number;
  };
};

function getErrorMessage(message: string | string[] | undefined): string {
  if (Array.isArray(message)) {
    return message.join(", ");
  }

  if (typeof message === "string" && message.trim()) {
    return message;
  }

  return "Failed to load data.";
}

function toValidUserId(value: unknown): string {
  if (typeof value !== "string" && typeof value !== "number") {
    return "";
  }

  const userId = Number(value);

  if (!Number.isInteger(userId) || userId < 1) {
    return "";
  }

  return String(userId);
}

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

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

  const directKeys = [
    "userId",
    "user_id",
    "currentUserId",
    "authUserId",
  ];

  for (const key of directKeys) {
    const userId = toValidUserId(window.localStorage.getItem(key));

    if (userId) {
      return userId;
    }
  }

  const objectKeys = [
    "user",
    "authUser",
    "currentUser",
    "auth",
    "profile",
    "session",
  ];

  for (const key of objectKeys) {
    const rawValue = window.localStorage.getItem(key);

    if (!rawValue) {
      continue;
    }

    try {
      const parsed = JSON.parse(rawValue) as {
        id?: unknown;
        userId?: unknown;
        user?: {
          id?: unknown;
          userId?: unknown;
        };
        data?: {
          id?: unknown;
          userId?: unknown;
          user?: {
            id?: unknown;
            userId?: unknown;
          };
        };
      };

      const userId =
        toValidUserId(parsed.id) ||
        toValidUserId(parsed.userId) ||
        toValidUserId(parsed.user?.id) ||
        toValidUserId(parsed.user?.userId) ||
        toValidUserId(parsed.data?.id) ||
        toValidUserId(parsed.data?.userId) ||
        toValidUserId(parsed.data?.user?.id) ||
        toValidUserId(parsed.data?.user?.userId);

      if (userId) {
        return userId;
      }
    } catch {
      // Ignore invalid localStorage JSON.
    }
  }

  return "";
}

async function getCurrentLoginUserId(): Promise<string> {
  try {
    const response = await fetch(`${API_BASE_URL}/auth/me`, {
      method: "GET",
      credentials: "include",
      cache: "no-store",
    });

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

    if (response.ok) {
      const userId =
        toValidUserId(result.data?.user?.id) ||
        toValidUserId(result.data?.user?.userId) ||
        toValidUserId(result.data?.id) ||
        toValidUserId(result.data?.userId) ||
        toValidUserId(result.user?.id) ||
        toValidUserId(result.user?.userId) ||
        toValidUserId(result.id);

      if (userId) {
        return userId;
      }
    }
  } catch {
    // Continue with localStorage fallback.
  }

  const userIdFromStorage = getUserIdFromStorage();

  if (userIdFromStorage) {
    return userIdFromStorage;
  }

  throw new Error("Login user ID not found. Please login again.");
}

async function getHeaders(): Promise<HeadersInit> {
  const userId = await getCurrentLoginUserId();

  return {
    "Content-Type": "application/json",
    "x-user-id": userId,
  };
}

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

  window.dispatchEvent(new Event("system-notification-updated"));
}

function getListItems<T>(response: unknown): T[] {
  if (!response || typeof response !== "object") {
    return [];
  }

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

  if (Array.isArray(value.data)) {
    return value.data as T[];
  }

  if (Array.isArray(value.items)) {
    return value.items as T[];
  }

  if (value.data && typeof value.data === "object") {
    const nestedData = value.data as Record<string, unknown>;

    if (Array.isArray(nestedData.items)) {
      return nestedData.items as T[];
    }

    if (Array.isArray(nestedData.data)) {
      return nestedData.data as T[];
    }

    const nestedItems = getListItems<T>(nestedData);

    if (nestedItems.length > 0) {
      return nestedItems;
    }
  }

  return [];
}

function normalizePlenaryStatus(status: ApiStatus): "Sent" | "Draft" {
  const normalized = String(status ?? "").trim().toUpperCase();

  return normalized === "SENT" ? "Sent" : "Draft";
}

function normalizeRgcDecisionStatus(
  status: string | null | undefined,
): RgcDecision["status"] {
  const normalized = String(status ?? "")
    .trim()
    .toUpperCase()
    .replace(/[\s-]+/g, "_");

  if (
    normalized === "NOT_ADDRESSED" ||
    normalized === "DRAFT"
  ) {
    return "Not Addressed" as RgcDecision["status"];
  }

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

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

  return "Not Addressed" as RgcDecision["status"];
}

function normalizeCreateStatus(value: string): string {
  const normalized = value.trim().toUpperCase().replace(/[\s-]+/g, "_");

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

  if (normalized === "IN_PROGRESS") {
    return "IN_PROGRESS";
  }

  return "NOT_ADDRESSED";
}

function formatDate(value: string | null | undefined): string {
  if (!value) {
    return "-";
  }

  const date = new Date(value);

  if (Number.isNaN(date.getTime())) {
    return "-";
  }

  return new Intl.DateTimeFormat("en-GB", {
    day: "2-digit",
    month: "short",
    year: "numeric",
    timeZone: "UTC",
  }).format(date);
}

function hasSavedTime(value: string | null | undefined): boolean {
  if (!value) {
    return false;
  }

  const match = value.match(
    /T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$/i,
  );

  if (!match) {
    return false;
  }

  const hour = Number(match[1]);
  const minute = Number(match[2]);
  const second = Number(match[3] ?? "0");

  return hour !== 0 || minute !== 0 || second !== 0;
}

function formatMeetingDateTime(value: string | null | undefined): string {
  if (!value) {
    return "-";
  }

  const date = new Date(value);

  if (Number.isNaN(date.getTime())) {
    return "-";
  }

  if (!hasSavedTime(value)) {
    return formatDate(value);
  }

  const parts = new Intl.DateTimeFormat("en-US", {
    month: "long",
    day: "numeric",
    year: "numeric",
    hour: "numeric",
    minute: "2-digit",
    hour12: true,
    timeZone: DISPLAY_TIME_ZONE,
  }).formatToParts(date);

  const getPart = (type: string): string =>
    parts.find((part) => part.type === type)?.value ?? "";

  const month = getPart("month");
  const day = getPart("day");
  const year = getPart("year");
  const hour = getPart("hour");
  const minute = getPart("minute");
  const dayPeriod = getPart("dayPeriod");

  return `${month} ${day}, ${year} at ${hour}:${minute} ${dayPeriod}`;
}

function getDocumentName(
  documentReference: string | null | undefined,
): string {
  if (!documentReference) {
    return "Plenary Document";
  }

  const [path, query = ""] = documentReference.split("?");

  const originalName = new URLSearchParams(query).get("originalName");

  return (
    originalName?.trim() ||
    path.replace(/\\/g, "/").split("/").pop() ||
    "Plenary Document"
  );
}

function removeHtmlTags(value: string | null | undefined): string {
  if (!value) {
    return "";
  }

  return value
    .replace(/<br\s*\/?>/gi, "\n")
    .replace(/<\/p>/gi, "\n")
    .replace(/<[^>]*>/g, " ")
    .replace(/&nbsp;/gi, " ")
    .replace(/&amp;/gi, "&")
    .replace(/&lt;/gi, "<")
    .replace(/&gt;/gi, ">")
    .replace(/\s+/g, " ")
    .trim();
}

function getCategoryName(item: ApiRgcDecisionItem): string {
  if (typeof item.category === "string" && item.category.trim()) {
    return item.category.trim();
  }

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

  if (item.categoryInfo?.name?.trim()) {
    return item.categoryInfo.name.trim();
  }

  return "-";
}

function getIndicatorName(item: ApiRgcDecisionItem): string {
  if (
    typeof item.indicator === "string" &&
    item.indicator.trim()
  ) {
    return item.indicator.trim();
  }

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

  if (item.indicatorName?.trim()) {
    return item.indicatorName.trim();
  }

  return "-";
}

function getVerificationLink(item: ApiRgcDecisionItem): string {
  return (
    item.verificationLink?.trim() ||
    item.linkToVerificationSource?.trim() ||
    item.verificationSourceLink?.trim() ||
    ""
  );
}

function mapRgcDecision(item: ApiRgcDecisionItem): RgcDecision {
  const mappedIssues: DecisionIssueItem[] = Array.isArray(item.issues)
    ? item.issues.map((i) => ({
        id: Number(i.id) || 0,
        title: i.title?.trim() || i.name?.trim() || `Issue ${i.id ?? 1}`,
        description: i.description?.trim() || null,
      }))
    : [];

  return {
    id: item.id,
    title: removeHtmlTags(item.decision) || "RGC Decision",
    decisionDate: formatDate(item.meetingDate),
    category: getCategoryName(item),
    status: normalizeRgcDecisionStatus(item.statusCode ?? item.status),
    focalPerson: item.focalPerson?.trim() || "-",

    indicatorId: item.indicatorId ?? null,
    indicator: item.indicator ?? null,
    indicatorName: getIndicatorName(item),

    verificationSource:
      item.verificationSource?.trim() || "-",
    sourceOfVerification:
      item.verificationSource?.trim() || "-",
    verificationLink: getVerificationLink(item),
    issues: mappedIssues, // 🔥 បញ្ជូនបញ្ជី Issues ដែលបាន Map រួចរាល់
  } as RgcDecision;
}

function createCountOnlyRgcDecisions(
  total: number,
): RgcDecision[] {
  return Array.from(
    {
      length: Math.max(0, total),
    },
    (_, index) => {
      const placeholder = {
        id: -(index + 1),
        title: "",
        decisionDate: "",
        category: "",
        status: "Not Addressed",
        focalPerson: "",
        indicatorName: "-",
        verificationSource: "-",
        sourceOfVerification: "-",
        verificationLink: "",
        issues: [],
      };

      return placeholder as unknown as RgcDecision;
    },
  );
}

function mapPlenary(
  item: ApiPlenaryItem,
  options?: {
    showMeetingTime?: boolean;
    rgcDecisions?: RgcDecision[];
  },
): PlenaryRecord {
  const totalRgcDecisions = Math.max(
    0,
    Number(item.numberOfRgcDecisions ?? 0),
  );

  return {
    id: item.id,
    name: item.name,
    meetingDate: options?.showMeetingTime
      ? formatMeetingDateTime(item.meetingDate)
      : formatDate(item.meetingDate),
    deadline: options?.showMeetingTime
      ? formatMeetingDateTime(item.deadline)
      : formatDate(item.deadline),
    status: normalizePlenaryStatus(item.statusCode ?? item.status),
    documentName: getDocumentName(item.documentReference),
    documentSize: "",
    description: "",
    rgcDecisions:
      options?.rgcDecisions ?? createCountOnlyRgcDecisions(totalRgcDecisions),
  };
}

export async function getRgcDecisionCategories(): Promise<
  RgcDecisionCategory[]
> {
  const endpoints = [
    `${API_BASE_URL}/rgc-decisions/lookups/categories`,
    `${API_BASE_URL}/working-group-issues/categories`,
    `${API_BASE_URL}/categories?limit=100`,
    `${API_BASE_URL}/categories`,
  ];

  let lastError = "Unable to load categories.";

  for (const endpoint of endpoints) {
    try {
      const response = await fetch(endpoint, {
        method: "GET",
        headers: await getHeaders(),
        credentials: "include",
        cache: "no-store",
      });

      const result =
        await readResponseJson<ApiListResponse<RgcDecisionCategory>>(
          response,
        );

      if (!response.ok) {
        lastError = getErrorMessage(result.message);
        continue;
      }

      const items = getListItems<RgcDecisionCategory>(result)
        .map((item) => ({
          id: Number(item.id),
          name: item.name?.trim() || `Category ${item.id}`,
        }))
        .filter(
          (item) =>
            Number.isInteger(item.id) &&
            item.id > 0 &&
            Boolean(item.name),
        );

      if (items.length > 0) {
        return items;
      }

      lastError = "Category API returned an empty list.";
    } catch (error) {
      lastError =
        error instanceof Error
          ? error.message
          : "Unable to load categories.";
    }
  }

  throw new Error(lastError);
}

export async function getRgcDecisionIndicators(): Promise<
  RgcDecisionIndicator[]
> {
  const endpoints = [
    `${API_BASE_URL}/indicators/options`,
    `${API_BASE_URL}/indicators?limit=100`,
    `${API_BASE_URL}/indicators`,
  ];

  const errors: string[] = [];

  for (const endpoint of endpoints) {
    try {
      const response = await fetch(endpoint, {
        method: "GET",
        headers: await getHeaders(),
        credentials: "include",
        cache: "no-store",
      });

      const result =
        await readResponseJson<ApiListResponse<RgcDecisionIndicator>>(
          response,
        );

      if (!response.ok) {
        errors.push(
          `${endpoint}: ${getErrorMessage(result.message)}`,
        );
        continue;
      }

      const items = getListItems<RgcDecisionIndicator>(result)
        .map((item) => ({
          id: Number(item.id),
          name: item.name?.trim() || `Indicator ${item.id}`,
          description: item.description?.trim() || null,
        }))
        .filter(
          (item) =>
            Number.isInteger(item.id) &&
            item.id > 0 &&
            Boolean(item.name),
        );

      if (items.length > 0) {
        return items;
      }

      errors.push(`${endpoint}: empty indicator list`);
    } catch (error) {
      errors.push(
        `${endpoint}: ${
          error instanceof Error
            ? error.message
            : "Unable to load indicators"
        }`,
      );
    }
  }

  throw new Error(
    errors.at(-1) || "Unable to load indicators.",
  );
}

export async function getMinistryPlenaries(): Promise<PlenaryRecord[]> {
  const params = new URLSearchParams({
    ministryOnly: "true",
    page: "1",
    limit: "100",
  });

  const response = await fetch(`${API_BASE_URL}/plenaries?${params}`, {
    method: "GET",
    headers: await getHeaders(),
    credentials: "include",
    cache: "no-store",
  });

  const result = await readResponseJson<ApiListResponse<ApiPlenaryItem>>(
    response,
  );

  if (!response.ok) {
    throw new Error(getErrorMessage(result.message));
  }

  return getListItems<ApiPlenaryItem>(result)
    .filter(
      (item) =>
        normalizePlenaryStatus(item.statusCode ?? item.status) === "Sent",
    )
    .map((item) =>
      mapPlenary(item, {
        showMeetingTime: false,
      }),
    );
}

export async function getMinistryPlenaryDetail(
  plenaryId: number,
): Promise<PlenaryRecord> {
  if (!Number.isInteger(plenaryId) || plenaryId < 1) {
    throw new Error("Invalid plenary ID.");
  }

  const headers = await getHeaders();

  const rgcParams = new URLSearchParams({
    plenaryId: String(plenaryId),
    page: "1",
    limit: "100",
  });

  const [plenaryResponse, rgcDecisionResponse] = await Promise.all([
    fetch(`${API_BASE_URL}/plenaries/${plenaryId}`, {
      method: "GET",
      headers,
      credentials: "include",
      cache: "no-store",
    }),

    // 🔥 ហៅទាញយក Data Decisions តាម plenaryId យ៉ាងជាក់លាក់ (មិនថាតែ Create ពី Ministry ឬ CDC G-PSF ទេ គឺទាញយកទាំងអស់មកបង្ហាញ)
    fetch(`${API_BASE_URL}/rgc-decisions?${rgcParams.toString()}`, {
      method: "GET",
      headers,
      credentials: "include",
      cache: "no-store",
    }),
  ]);

  const [plenaryResult, rgcDecisionResult] = await Promise.all([
    readResponseJson<ApiDetailResponse<ApiPlenaryItem>>(plenaryResponse),
    readResponseJson<ApiListResponse<ApiRgcDecisionItem>>(
      rgcDecisionResponse,
    ),
  ]);

  if (!plenaryResponse.ok) {
    throw new Error(getErrorMessage(plenaryResult.message));
  }

  if (!rgcDecisionResponse.ok) {
    throw new Error(getErrorMessage(rgcDecisionResult.message));
  }

  const plenary = plenaryResult.data ?? (plenaryResult as ApiPlenaryItem);

  if (!plenary?.id) {
    throw new Error("Plenary detail data not found.");
  }

  const rgcDecisions = getListItems<ApiRgcDecisionItem>(rgcDecisionResult)
    .filter((item) => Number(item.id) > 0)
    .map(mapRgcDecision);

  return mapPlenary(plenary, {
    showMeetingTime: true,
    rgcDecisions,
  });
}

export async function createMinistryRgcDecision(
  input: CreateMinistryRgcDecisionInput,
): Promise<ApiRgcDecisionItem> {
  const decision = input.decision.trim();
  const meetingDate = input.meetingDate.trim();
  const focalPerson = input.focalPerson.trim();
  const verificationSource = input.verificationSource.trim();
  const verificationLink = input.verificationLink?.trim() || "";

  if (!Number.isInteger(input.plenaryId) || input.plenaryId < 1) {
    throw new Error("Invalid plenary ID.");
  }

  if (!Number.isInteger(input.categoryId) || input.categoryId < 1) {
    throw new Error("Please select a category.");
  }

  if (!Number.isInteger(input.indicatorId) || input.indicatorId < 1) {
    throw new Error("Please select an indicator.");
  }

  if (!meetingDate) {
    throw new Error("Meeting Date is required.");
  }

  if (!focalPerson) {
    throw new Error("Focal Person is required.");
  }

  if (!decision) {
    throw new Error("RGC's Decision is required.");
  }

  if (!verificationSource) {
    throw new Error("Source of Verification is required.");
  }

  const response = await fetch(`${API_BASE_URL}/rgc-decisions`, {
    method: "POST",
    headers: await getHeaders(),
    credentials: "include",
    cache: "no-store",
    body: JSON.stringify({
      plenaryId: input.plenaryId,
      categoryId: input.categoryId,
      indicatorId: input.indicatorId,
      meetingDate,
      status: normalizeCreateStatus(input.status),
      focalPerson,
      decision,
      verificationSource,
      verificationLink,
      issueIds: input.issueIds ?? [], // 🔥 បញ្ជូន issueIds ទៅ Backend
    }),
  });

  const result = await readResponseJson<
    ApiDetailResponse<ApiRgcDecisionItem>
  >(response);

  if (!response.ok) {
    throw new Error(getErrorMessage(result.message));
  }

  const createdDecision =
    result.data ?? (result as ApiRgcDecisionItem);

  if (!createdDecision?.id) {
    throw new Error(
      "Created RGC Decision ID was not returned from API.",
    );
  }

  return createdDecision;
}

export async function submitRgcDecisionNotificationToCdc(
  rgcDecisionId: number,
): Promise<SubmitRgcDecisionNotificationResponse> {
  if (!Number.isInteger(rgcDecisionId) || rgcDecisionId < 1) {
    throw new Error("Invalid RGC Decision ID.");
  }

  const response = await fetch(
    `${API_BASE_URL}/rgc-decisions/${rgcDecisionId}/submit-to-cdc`,
    {
      method: "POST",
      headers: await getHeaders(),
      credentials: "include",
      cache: "no-store",
      body: JSON.stringify({}),
    },
  );

  const result =
    await readResponseJson<SubmitRgcDecisionNotificationResponse & {
      message?: string | string[];
    }>(response);

  if (!response.ok) {
    throw new Error(getErrorMessage(result.message));
  }

  dispatchSystemNotificationUpdated();

  return result;
}

export async function submitMinistryPlenaryToCdc(
  plenaryId: number,
): Promise<ApiPlenaryItem> {
  if (!Number.isInteger(plenaryId) || plenaryId < 1) {
    throw new Error("Invalid plenary ID.");
  }

  const response = await fetch(
    `${API_BASE_URL}/plenaries/${plenaryId}/submit`,
    {
      method: "PATCH",
      headers: await getHeaders(),
      credentials: "include",
      cache: "no-store",
      body: JSON.stringify({}),
    },
  );

  const result = await readResponseJson<ApiDetailResponse<ApiPlenaryItem>>(
    response,
  );

  if (!response.ok) {
    throw new Error(getErrorMessage(result.message));
  }

  dispatchSystemNotificationUpdated();

  return result.data ?? (result as ApiPlenaryItem);
}