"use client";

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

export type EvaluationApiStatus = {
  id: number;
  code: string;
  name: string;
};

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

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

export type EvaluationApiGovernmentAgency = {
  agencyOrder: number;

  stakeholder: {
    id: number;
    name: string;
  };
};

export type SavedEvaluation = {
  id: number;
  issueId: number;
  isResolved: boolean;

  /**
   * API returns rating from 1 to 5.
   * Keep null support for old records that may not have a rating.
   */
  rating: number | null;

  feedback: unknown;
  userId: number;
  createdAt: string;
  updatedAt: string;
};

export type EvaluationIssue = {
  issueId: number;
  title: string;
  description: string;
  recommendation: string;

  status: EvaluationApiStatus | null;
  category: EvaluationApiCategory | null;
  workingGroup: EvaluationApiStakeholder | null;

  governmentAgencies: EvaluationApiGovernmentAgency[];

  evaluation: SavedEvaluation | null;
};

export type EvaluationAssignment = {
  id: number;
  progressReportId: number;
  ministryId: number;
  status: string;

  ministry: EvaluationApiStakeholder | null;

  progressReport: {
    id: number;
    title: string;
    description: unknown;
    year: number;
    semester: string;
    status: string;
  } | null;
};

export type EvaluationFormResponse = {
  assignment: EvaluationAssignment;
  totalIssues: number;
  items: EvaluationIssue[];
};

export type SaveEvaluationItem = {
  issueId: number;
  isResolved: boolean;
  rating: number;
  feedback: string;
};

export type SaveEvaluationPayload = {
  evaluations: SaveEvaluationItem[];
};

export type SavedEvaluationItem = {
  id: number;
  progressReportMinistryId: number;
  issueId: number;
  isResolved: boolean;
  rating: number | null;
  feedback: unknown;
  userId: number;

  issue?: {
    id: number;
    title: string;
    description?: string;
    recommendation?: string;

    issueStatus?: {
      id: number;
      code: string;
      name: string;
    } | null;

    category?: {
      id: number;
      name: string;
    } | null;
  };

  createdAt: string;
  updatedAt: string;
};

export type SaveEvaluationResponse = {
  success?: boolean;
  message?: string;

  assignmentId: number;
  progressReportId: number;
  ministryId: number;
  total: number;

  items: SavedEvaluationItem[];
};

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

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 getHeaders(hasBody = false): HeadersInit {
  const token = getAccessToken();

  return {
    Accept: "application/json",

    ...(hasBody
      ? {
          "Content-Type": "application/json",
        }
      : {}),

    ...(token
      ? {
          Authorization: `Bearer ${token}`,
        }
      : {}),
  };
}

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

  return message?.trim() || fallback;
}

async function apiRequest<T>(
  path: string,
  options: RequestInit = {},
): Promise<T> {
  const response = await fetch(`${API_BASE_URL}${path}`, {
    ...options,

    cache: "no-store",

    credentials: "include",

    headers: {
      ...getHeaders(Boolean(options.body)),
      ...(options.headers ?? {}),
    },
  });

  let payload: ApiResponse<T>;

  try {
    payload = (await response.json()) as ApiResponse<T>;
  } catch {
    throw new Error(
      `API returned invalid JSON. Status ${response.status}.`,
    );
  }

  if (!response.ok || payload.success === false) {
    throw new Error(
      getMessage(
        payload.message,
        `Request failed with status ${response.status}.`,
      ),
    );
  }

  if (payload.data === undefined) {
    throw new Error("API response does not contain data.");
  }

  return payload.data;
}

function validateAssignmentId(assignmentId: number): number {
  const parsed = Number(assignmentId);

  if (!Number.isInteger(parsed) || parsed <= 0) {
    throw new Error("Invalid progress report assignment ID.");
  }

  return parsed;
}

/**
 * GET
 * /progress-reports/assignments/:assignmentId/evaluations/form
 */
export async function getEvaluationForm(
  assignmentId: number,
): Promise<EvaluationFormResponse> {
  const id = validateAssignmentId(assignmentId);

  return apiRequest<EvaluationFormResponse>(
    `/progress-reports/assignments/${id}/evaluations/form`,
    {
      method: "GET",
    },
  );
}

/**
 * GET
 * /progress-reports/assignments/:assignmentId/evaluations
 */
export async function getEvaluations(
  assignmentId: number,
): Promise<SaveEvaluationResponse> {
  const id = validateAssignmentId(assignmentId);

  return apiRequest<SaveEvaluationResponse>(
    `/progress-reports/assignments/${id}/evaluations`,
    {
      method: "GET",
    },
  );
}

/**
 * POST
 * /progress-reports/assignments/:assignmentId/evaluations
 */
export async function saveEvaluations(
  assignmentId: number,
  payload: SaveEvaluationPayload,
): Promise<SaveEvaluationResponse> {
  const id = validateAssignmentId(assignmentId);

  return apiRequest<SaveEvaluationResponse>(
    `/progress-reports/assignments/${id}/evaluations`,
    {
      method: "POST",
      body: JSON.stringify(payload),
    },
  );
}