"use client";

import type {
  RgcDecisionRow,
  RgcDecisionStatus,
} from "../data/rgc-decision-data";

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

const CEFP_FALLBACK_USER_ID = 5;

type UnknownObject = Record<string, unknown>;

export type RgcDecisionLookupOption = {
  id: number;
  label: string;
};

export type RgcDecisionLookups = {
  plenaries: RgcDecisionLookupOption[];
  ministries: RgcDecisionLookupOption[];
  categories: RgcDecisionLookupOption[];
};

export type CreateCefpRgcDecisionPayload = {
  plenaryId: number;
  stakeholderId: number;
  categoryId: number;
  indicatorId?: number;
  meetingDate: string;
  status: string;
  focalPerson: string;
  decision: string;
  verificationSource?: string;
  verificationLink?: string;
};

// Compatibility with the shared dialog component.
export type CreateCdcRgcDecisionPayload = CreateCefpRgcDecisionPayload;

function isObject(value: unknown): value is UnknownObject {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}

function toPositiveInteger(value: unknown): number | null {
  const parsed = Number(value);
  return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
}

function normalizeRole(value: unknown): string {
  return String(value ?? "")
    .trim()
    .toLowerCase()
    .replace(/[\s-]+/g, "_");
}

function extractUserObject(value: unknown): UnknownObject | null {
  if (!isObject(value)) return null;
  if (isObject(value.user)) return value.user;
  if (isObject(value.data)) {
    if (isObject(value.data.user)) return value.data.user;
    return value.data;
  }
  return value;
}

function parseStoredUser(raw: string | null): UnknownObject | null {
  if (!raw) return null;
  try {
    return extractUserObject(JSON.parse(raw));
  } catch {
    return null;
  }
}

function getRoleNames(user: UnknownObject): string[] {
  const result: string[] = [];
  const candidates = [user.role, user.roleName, user.roles, user.userRoles];

  for (const candidate of candidates) {
    if (typeof candidate === "string") result.push(normalizeRole(candidate));
    if (Array.isArray(candidate)) {
      for (const item of candidate) {
        if (typeof item === "string") result.push(normalizeRole(item));
        if (isObject(item)) {
          result.push(normalizeRole(item.name));
          if (isObject(item.role)) result.push(normalizeRole(item.role.name));
        }
      }
    }
    if (isObject(candidate)) result.push(normalizeRole(candidate.name));
  }

  return result.filter(Boolean);
}

function extractUserId(user: UnknownObject): number | null {
  return (
    toPositiveInteger(user.id) ??
    toPositiveInteger(user.userId) ??
    (isObject(user.profile) ? toPositiveInteger(user.profile.id) : null)
  );
}

export function getCurrentCefpUserId(): number {
  if (typeof window === "undefined") return CEFP_FALLBACK_USER_ID;

  const keys = [
    "currentUser",
    "authUser",
    "user",
    "profile",
    "auth",
    "loginResponse",
  ];

  for (const key of keys) {
    const user =
      parseStoredUser(localStorage.getItem(key)) ??
      parseStoredUser(sessionStorage.getItem(key));

    if (!user) continue;

    const roles = getRoleNames(user);
    const isCefp = roles.some((role) =>
      ["cefp", "cefp_secretariat"].includes(role),
    );

    if (isCefp) {
      const id = extractUserId(user);
      if (id) return id;
    }
  }

  // The current seeded CEFP account is user ID 5.
  return CEFP_FALLBACK_USER_ID;
}

function buildHeaders(includeJson = false): HeadersInit {
  return {
    Accept: "application/json",
    "x-user-id": String(getCurrentCefpUserId()),
    ...(includeJson ? { "Content-Type": "application/json" } : {}),
  };
}

function readString(object: UnknownObject | null, keys: string[]): string {
  if (!object) return "";
  for (const key of keys) {
    const value = object[key];
    if (typeof value === "string" && value.trim()) return value.trim();
    if (typeof value === "number" && Number.isFinite(value)) return String(value);
  }
  return "";
}

function readObject(object: UnknownObject | null, keys: string[]): UnknownObject | null {
  if (!object) return null;
  for (const key of keys) {
    if (isObject(object[key])) return object[key] as UnknownObject;
  }
  return null;
}

function extractArray(payload: unknown): UnknownObject[] {
  if (Array.isArray(payload)) return payload.filter(isObject);
  if (!isObject(payload)) return [];

  for (const key of ["items", "results", "rows", "records", "list", "data"]) {
    const value = payload[key];
    if (Array.isArray(value)) return value.filter(isObject);
  }

  if (isObject(payload.data)) {
    for (const key of ["items", "results", "rows", "records", "list", "data"]) {
      const value = payload.data[key];
      if (Array.isArray(value)) return value.filter(isObject);
    }
  }

  return [];
}

function collectMessages(value: unknown): string[] {
  if (typeof value === "string") return value.trim() ? [value.trim()] : [];
  if (Array.isArray(value)) return value.flatMap(collectMessages);
  if (!isObject(value)) return [];
  return [
    ...collectMessages(value.message),
    ...collectMessages(value.messages),
    ...collectMessages(value.errors),
    ...collectMessages(value.details),
  ];
}

function apiError(payload: unknown, fallback: string): string {
  const messages = Array.from(new Set(collectMessages(payload)));
  return messages.length ? messages.join(", ") : fallback;
}

function normalizeDate(value: unknown): string {
  const text = typeof value === "string" ? value.trim() : "";
  const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(text);
  return match ? `${match[1]}-${match[2]}-${match[3]}` : text;
}

function normalizeStatus(value: unknown): RgcDecisionStatus {
  const normalized = String(value ?? "")
    .trim()
    .toUpperCase()
    .replaceAll("_", " ")
    .replaceAll("-", " ");
  if (normalized === "SOLVED") return "Solved";
  if (normalized === "IN PROGRESS") return "In Progress";
  return "Not Addressed";
}

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 mapItem(item: UnknownObject): RgcDecisionRow | null {
  const id =
    toPositiveInteger(item.id) ??
    toPositiveInteger(item.decisionId) ??
    toPositiveInteger(item.rgcDecisionId);
  if (!id) return null;

  const stakeholder = readObject(item, ["stakeholder", "ministry", "primaryAgency", "agency"]);
  const categoryObject = readObject(item, ["categoryInfo", "category", "measureCategory"]);
  const indicatorObject = readObject(item, ["indicator"]);
  const plenaryObject = readObject(item, ["plenary"]);

  const ministry =
    readString(item, ["ministryName", "stakeholderName", "primaryAgencyName", "agencyName"]) ||
    readString(stakeholder, ["name", "title", "label", "shortName"]);

  const meetingDate = normalizeDate(
    readString(item, ["meetingDate", "effectiveDate", "decisionDate", "date"]) ||
      readString(plenaryObject, ["meetingDate", "effectiveDate", "date"]),
  );

  const verificationLink =
    readString(item, [
      "verificationLink",
      "verificationDownloadUrl",
      "downloadUrl",
      "linkToVerificationSource",
      "sourceLink",
    ]) || null;

  const category =
    readString(item, ["categoryName", "measureCategory"]) ||
    readString(categoryObject, ["name", "title", "label"]) ||
    (typeof item.category === "string" ? item.category.trim() : "");

  return {
    id,
    plenaryId: toPositiveInteger(item.plenaryId) ?? toPositiveInteger(plenaryObject?.id),
    plenary:
      readString(item, ["plenaryName", "plenaryTitle"]) ||
      readString(plenaryObject, ["name", "title", "label"]),
    ministryId:
      toPositiveInteger(item.stakeholderId) ??
      toPositiveInteger(item.ministryId) ??
      toPositiveInteger(stakeholder?.id),
    ministry,
    ministryLogo:
      readString(item, ["ministryLogo", "stakeholderLogo", "agencyLogo", "logo"]) ||
      readString(stakeholder, ["logo", "logoUrl", "image", "imageUrl"]) ||
      null,
    decision: readString(item, ["decision", "rgcDecision", "decisionText", "description", "content", "title"]),
    meetingDate,
    categoryId: toPositiveInteger(item.categoryId) ?? toPositiveInteger(categoryObject?.id),
    category,
    status: normalizeStatus(item.statusCode ?? item.status ?? item.decisionStatus),
    indicatorId: toPositiveInteger(item.indicatorId) ?? toPositiveInteger(indicatorObject?.id),
    indicator:
      readString(item, ["indicatorName"]) ||
      readString(indicatorObject, ["name", "title", "label"]),
    focalPerson: readString(item, ["focalPerson", "focalPersonName", "focalPersonHe", "focalPersonHE"]),
    sourceOfVerification: readString(item, ["verificationSource", "sourceOfVerification", "verification"]),
    verificationLink,
    verificationDownloadUrl: verificationLink,
    primaryAgency: ministry,
    measureCategory: category,
    effectiveDate: normalizeDate(item.effectiveDate) || meetingDate,
  };
}

async function requestJson(url: string): Promise<unknown> {
  const response = await fetch(url, {
    method: "GET",
    credentials: "include",
    cache: "no-store",
    headers: buildHeaders(),
  });
  const payload = await response.json().catch(() => null);
  if (!response.ok) throw new Error(apiError(payload, `Request failed (${response.status}).`));
  return payload;
}

async function fetchLookup(endpoint: string): Promise<RgcDecisionLookupOption[]> {
  const payload = await requestJson(`${API_BASE_URL}${endpoint}`);
  const map = new Map<number, RgcDecisionLookupOption>();
  for (const item of extractArray(payload)) {
    const id = toPositiveInteger(item.id) ?? toPositiveInteger(item.value);
    const label = readString(item, ["name", "label", "title", "shortName"]);
    if (id && label) map.set(id, { id, label });
  }
  return Array.from(map.values()).sort((a, b) => a.label.localeCompare(b.label));
}

export async function getCefpRgcDecisions(): Promise<RgcDecisionRow[]> {
  // CEFP can see the complete RGC Decision list.
  const payload = await requestJson(`${API_BASE_URL}/rgc-decisions?limit=100`);
  return extractArray(payload).map(mapItem).filter((item): item is RgcDecisionRow => item !== null);
}

export const getCdcRgcDecisions = getCefpRgcDecisions;

export async function getCefpRgcDecisionLookups(): Promise<RgcDecisionLookups> {
  const [plenaries, ministries, categories] = await Promise.all([
    fetchLookup("/rgc-decisions/lookups/plenaries"),
    fetchLookup("/rgc-decisions/lookups/ministries"),
    fetchLookup("/rgc-decisions/lookups/categories"),
  ]);
  return { plenaries, ministries, categories };
}

export const getCdcRgcDecisionLookups = getCefpRgcDecisionLookups;

export async function createCefpRgcDecision(
  payload: CreateCefpRgcDecisionPayload,
): Promise<unknown> {
  if (!Number.isInteger(payload.plenaryId) || payload.plenaryId < 1) {
    throw new Error("Please select a valid Plenary.");
  }
  if (!Number.isInteger(payload.stakeholderId) || payload.stakeholderId < 1) {
    throw new Error("Please select a valid Ministry.");
  }
  if (!Number.isInteger(payload.categoryId) || payload.categoryId < 1) {
    throw new Error("Please select a valid Category.");
  }

  const requestBody = {
    plenaryId: payload.plenaryId,
    stakeholderId: payload.stakeholderId,
    categoryId: payload.categoryId,
    ...(payload.indicatorId ? { indicatorId: payload.indicatorId } : {}),
    meetingDate: normalizeDate(payload.meetingDate),
    status: normalizeCreateStatus(payload.status),
    focalPerson: payload.focalPerson.trim(),
    decision: payload.decision.trim(),
    verificationSource: payload.verificationSource?.trim() || undefined,
    verificationLink: payload.verificationLink?.trim() || undefined,
  };

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

  const text = await response.text();
  let result: unknown = null;
  if (text.trim()) {
    try {
      result = JSON.parse(text);
    } catch {
      result = text.trim();
    }
  }

  if (!response.ok) {
    throw new Error(apiError(result, `Unable to create RGC Decision (${response.status}).`));
  }
  return result;
}

export const createCdcRgcDecision = createCefpRgcDecision;
