import { replaceAdminHref } from "./app-path";
import { recordOnlineClassApiError } from "./online-class-api-log";
import {
  clearAcademicContext,
  getAcademicYearId,
  getAcademicYears,
  setAcademicContext,
  type AcademicContextState,
} from "./academic-context";
import {
  clearAllSessionStorage,
  getActiveBranchId,
  getActiveRole,
  isImpersonating,
  notifySessionChanged,
  restoreImpersonationBackup,
} from "./session-context";

const API_BASE =
  process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000/api/v1";

export const PORTAL_APP_URL =
  process.env.NEXT_PUBLIC_PORTAL_URL ?? "http://localhost:3000";

export class ApiError extends Error {
  constructor(
    message: string,
    public status: number,
    public payload?: unknown,
  ) {
    super(message);
    this.name = "ApiError";
  }
}

export function allowsAppTarget(
  data: { app_target?: string; app_targets?: string[] },
  app: "admin" | "portal",
): boolean {
  const targets =
    Array.isArray(data.app_targets) && data.app_targets.length > 0
      ? data.app_targets
      : data.app_target
        ? [data.app_target]
        : [];
  return targets.includes(app);
}

export function isPortalRoleName(role?: string | null): boolean {
  return role === "parent" || role === "student";
}

export function retryAfterSeconds(error: ApiError): number {
  const payload = error.payload;
  if (!payload || typeof payload !== "object") {
    return 0;
  }
  const rec = payload as { errors?: { retry_after?: number }; data?: { retry_after?: number } };
  const value = rec.errors?.retry_after ?? rec.data?.retry_after;
  return typeof value === "number" && value > 0 ? Math.ceil(value) : 0;
}

export function getToken(): string | null {
  if (typeof window === "undefined") return null;
  return localStorage.getItem("admin_token");
}

export function setToken(token: string) {
  localStorage.setItem("admin_token", token);
  notifySessionChanged();
}

let sessionExpiredRedirect = false;
let suppressAuthExpire = 0;

export async function withoutAuthExpireRedirect<T>(fn: () => Promise<T>): Promise<T> {
  suppressAuthExpire += 1;
  try {
    return await fn();
  } finally {
    suppressAuthExpire = Math.max(0, suppressAuthExpire - 1);
  }
}

function expireSessionAndRedirectToLogin() {
  if (typeof window === "undefined") return;
  if (window.location.pathname.endsWith("/login")) return;
  if (window.location.pathname.startsWith("/join")) return;
  if (sessionExpiredRedirect) return;
  if (suppressAuthExpire > 0) return;
  if (restoreImpersonationBackup()) {
    replaceAdminHref("/staff");
    return;
  }
  sessionExpiredRedirect = true;
  clearToken();
  replaceAdminHref("/login");
}

function redirectIfUnauthorized(response: Response) {
  if (response.status !== 401) return;
  try {
    const url = new URL(response.url);
    if (url.pathname.includes("/auth/")) return;
  } catch {
    return;
  }
  expireSessionAndRedirectToLogin();
}

export function clearToken() {
  localStorage.removeItem("admin_token");
  localStorage.removeItem("admin_user");
  clearAcademicContext();
  clearAllSessionStorage();
}

export function getStoredUser<T>(): T | null {
  if (typeof window === "undefined") return null;
  const raw = localStorage.getItem("admin_user");
  if (!raw) return null;
  try {
    return JSON.parse(raw) as T;
  } catch {
    return null;
  }
}

export function setStoredUser(user: unknown) {
  localStorage.setItem("admin_user", JSON.stringify(user));
}

function redirectIfPasswordChangeRequired(response: Response, payload: unknown) {
  if (typeof window === "undefined") return;
  if (isImpersonating()) return;
  if (response.status !== 403) return;
  if (!payload || typeof payload !== "object") return;
  if ((payload as { code?: string }).code !== "PASSWORD_CHANGE_REQUIRED") return;
  if (window.location.pathname.endsWith("/change-password")) return;
  replaceAdminHref("/change-password");
}

async function trackedFetch(method: string, path: string, init: RequestInit): Promise<Response> {
  try {
    return await fetch(`${API_BASE}${path}`, init);
  } catch (err) {
    recordOnlineClassApiError({
      method,
      path,
      status: 0,
      message: err instanceof Error ? err.message : "ارتباط با سرور برقرار نشد",
      payload: null,
    });
    throw err;
  }
}

async function parseJson<T>(response: Response, meta?: { method: string; path: string }): Promise<T> {
  const payload = await response.json().catch(() => ({}));
  redirectIfUnauthorized(response);
  redirectIfPasswordChangeRequired(response, payload);

  if (!response.ok) {
    const rawMessage = (payload as { message?: string }).message;
    const message =
      rawMessage && rawMessage.trim() !== ""
        ? rawMessage
        : response.status === 403
          ? "اجازه این عملیات را ندارید."
          : "خطا در ارتباط با سرور";
    if (meta) {
      recordOnlineClassApiError({
        method: meta.method,
        path: meta.path,
        status: response.status,
        message,
        payload,
      });
    }
    throw new ApiError(message, response.status, payload);
  }

  return (payload as { data: T }).data;
}

function authHeaders(): HeadersInit {
  const token = getToken();
  const yearId = getAcademicYearId();
  const role = getActiveRole();
  const branchId = getActiveBranchId();
  return {
    Accept: "application/json",
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
    ...(yearId ? { "X-Academic-Year-Id": String(yearId) } : {}),
    ...(role ? { "X-Active-Role": role } : {}),
    ...(branchId ? { "X-Branch-Id": String(branchId) } : {}),
    "X-App-Target": "admin",
  };
}

export async function apiGet<T>(path: string): Promise<T> {
  const response = await trackedFetch("GET", path, {
    headers: authHeaders(),
    cache: "no-store",
  });

  return parseJson<T>(response, { method: "GET", path });
}

export async function apiPost<T>(path: string, body: unknown): Promise<T> {
  const response = await trackedFetch("POST", path, {
    method: "POST",
    headers: {
      ...authHeaders(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  return parseJson<T>(response, { method: "POST", path });
}

export async function apiPatch<T>(path: string, body: unknown): Promise<T> {
  const response = await trackedFetch("PATCH", path, {
    method: "PATCH",
    headers: {
      ...authHeaders(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  return parseJson<T>(response, { method: "PATCH", path });
}

export async function apiPut<T>(path: string, body: unknown): Promise<T> {
  const response = await trackedFetch("PUT", path, {
    method: "PUT",
    headers: {
      ...authHeaders(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  return parseJson<T>(response, { method: "PUT", path });
}

export async function apiDelete<T>(path: string): Promise<T> {
  const response = await trackedFetch("DELETE", path, {
    method: "DELETE",
    headers: authHeaders(),
  });

  return parseJson<T>(response, { method: "DELETE", path });
}

export async function apiPostFormData<T>(path: string, formData: FormData): Promise<T> {
  const response = await trackedFetch("POST", path, {
    method: "POST",
    headers: authHeaders(),
    body: formData,
  });

  return parseJson<T>(response, { method: "POST", path });
}

export async function uploadOrgLogo(file: File, branchId?: number | null): Promise<import("./types").Branding> {
  const formData = new FormData();
  formData.append("logo", file);
  const suffix = branchId ? `?branch_id=${branchId}` : "";
  return apiPostFormData(`/branding/logo${suffix}`, formData);
}

export async function deleteOrgLogo(branchId?: number | null): Promise<import("./types").Branding> {
  const suffix = branchId ? `?branch_id=${branchId}` : "";
  return apiDelete(`/branding/logo${suffix}`);
}

export async function uploadOrgFavicon(file: File): Promise<import("./types").Branding> {
  const formData = new FormData();
  formData.append("favicon", file);
  return apiPostFormData("/branding/favicon", formData);
}

export async function deleteOrgFavicon(): Promise<import("./types").Branding> {
  return apiDelete("/branding/favicon");
}

export async function uploadBranchLogo(
  branchId: number,
  file: File,
): Promise<import("./types").Branding> {
  const formData = new FormData();
  formData.append("logo", file);
  return apiPostFormData(`/branches/${branchId}/branding/logo`, formData);
}

export async function deleteBranchLogo(branchId: number): Promise<import("./types").Branding> {
  return apiDelete(`/branches/${branchId}/branding/logo`);
}

export interface BranchLogoMeta {
  inherits: boolean;
  logo_url: string | null;
}

export async function fetchBranchLogoMeta(branchId: number): Promise<BranchLogoMeta> {
  return apiGet(`/branches/${branchId}/branding/logo`);
}

export async function uploadUserAvatar(userId: number, file: File): Promise<{ avatar_path?: string | null }> {
  const formData = new FormData();
  formData.append("avatar", file);
  return apiPostFormData(`/users/${userId}/avatar`, formData);
}

export async function deleteUserAvatar(userId: number): Promise<{ avatar_path?: string | null }> {
  return apiDelete(`/users/${userId}/avatar`);
}

export async function apiDownloadFile(path: string, fallbackName = "download"): Promise<void> {
  const response = await fetch(`${API_BASE}${path}`, {
    headers: {
      ...authHeaders(),
      Accept: "*/*",
    },
  });

  if (!response.ok) {
    const payload = await response.json().catch(() => ({}));
    throw new ApiError(
      (payload as { message?: string }).message ?? "خطا در دانلود",
      response.status,
      payload,
    );
  }

  const disposition = response.headers.get("Content-Disposition");
  const match = disposition?.match(/filename[^;=\n]*=(['"]?)([^'"\n]*)\1/);
  const filename = match?.[2] ? decodeURIComponent(match[2]) : fallbackName;
  const blob = await response.blob();
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = filename;
  anchor.click();
  URL.revokeObjectURL(url);
}

export async function apiFetchBlob(path: string): Promise<{ blob: Blob; mime: string }> {
  const response = await fetch(`${API_BASE}${path}`, {
    headers: authHeaders(),
  });

  if (!response.ok) {
    const payload = await response.json().catch(() => ({}));
    throw new ApiError(
      (payload as { message?: string }).message ?? "خطا در بارگذاری فایل",
      response.status,
      payload,
    );
  }

  const mime = response.headers.get("Content-Type") ?? "application/octet-stream";
  const blob = await response.blob();
  return { blob, mime };
}

async function authPost<T>(path: string, body: Record<string, unknown>): Promise<T> {
  const response = await fetch(`${API_BASE}${path}`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  const payload = await response.json().catch(() => ({}));

  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "درخواست ناموفق بود.",
      response.status,
      payload,
    );
  }

  return (payload as { data: T }).data;
}

export async function requestLoginOtp(nationalCode: string, purpose: "login" | "forgot" = "login") {
  return authPost<{ sent?: boolean; otp_sent?: boolean; retry_after?: number }>("/auth/otp/request", {
    national_code: nationalCode,
    purpose,
  });
}

export async function fetchCaptcha() {
  const response = await fetch(`${API_BASE}/auth/captcha`, {
    headers: { Accept: "application/json" },
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "دریافت کپچا ناموفق بود.",
      response.status,
      payload,
    );
  }
  return (payload as { data: { key: string; question: string } }).data;
}

export async function forgotPassword(input: {
  national_code: string;
  captcha_key: string;
  captcha_answer: string;
  is_foreign_national?: boolean;
}) {
  const response = await fetch(`${API_BASE}/auth/forgot-password`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "درخواست ناموفق بود.",
      response.status,
      payload,
    );
  }
  return (payload as { message?: string }).message ?? "پیامک ارسال شد.";
}

export async function verifyForgotOtp(input: { national_code: string; otp: string }) {
  const response = await fetch(`${API_BASE}/auth/forgot-password/verify-otp`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "کد تأیید نامعتبر است.",
      response.status,
      payload,
    );
  }
  return (payload as { message?: string }).message ?? "کد تأیید صحیح است.";
}

export async function resetPassword(input: {
  national_code: string;
  password: string;
  password_confirmation: string;
}) {
  const response = await fetch(`${API_BASE}/auth/reset-password`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "بازنشانی رمز ناموفق بود.",
      response.status,
      payload,
    );
  }
  return (payload as { message?: string }).message ?? "رمز عبور تغییر کرد.";
}

/** @deprecated Use resetPassword after verifyForgotOtp */
export async function resetPasswordWithOtp(input: {
  national_code: string;
  otp: string;
  password: string;
  password_confirmation: string;
}) {
  const response = await fetch(`${API_BASE}/auth/reset-password`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "بازنشانی رمز ناموفق بود.",
      response.status,
      payload,
    );
  }
  return (payload as { message?: string }).message ?? "رمز عبور تغییر کرد.";
}

export async function verifyCredentials(nationalCode: string, password: string) {
  const response = await fetch(`${API_BASE}/auth/verify-credentials`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      national_code: nationalCode,
      password,
    }),
  });

  const payload = await response.json().catch(() => ({}));

  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "اطلاعات ورود نامعتبر است.",
      response.status,
      payload,
    );
  }

  return (payload as {
    data: {
      requires_otp: boolean;
      app_target: string;
      app_targets?: string[];
      otp_sent?: boolean;
      retry_after?: number;
    };
  }).data;
}

export async function login(nationalCode: string, password: string, otp?: string) {
  const response = await fetch(`${API_BASE}/auth/login`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      national_code: nationalCode,
      password,
      ...(otp ? { otp } : {}),
    }),
  });

  const payload = await response.json().catch(() => ({}));

  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "ورود ناموفق بود.",
      response.status,
      payload,
    );
  }

  const data = (payload as {
    data: {
      token: string;
      user: AdminUser;
      app_target: string;
      app_targets?: string[];
      academic_context?: AcademicContextState;
    };
  }).data;

  if (!allowsAppTarget(data, "admin")) {
    throw new ApiError("این حساب برای پنل مدیریت مجاز نیست.", 403);
  }

  clearAcademicContext();
  clearAllSessionStorage();
  setToken(data.token);
  setStoredUser(data.user);
  if (data.academic_context) {
    setAcademicContext(data.academic_context);
  }

  return data;
}

export async function requestPasswordChangeOtp() {
  return apiPost<{ otp_sent?: boolean; retry_after?: number }>("/auth/password/otp", {});
}

export async function changeOwnPassword(input: {
  password: string;
  password_confirmation: string;
  otp: string;
}) {
  const response = await fetch(`${API_BASE}/auth/password`, {
    method: "POST",
    headers: {
      ...authHeaders(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new ApiError(
      (payload as { message?: string }).message ?? "تغییر رمز عبور ناموفق بود.",
      response.status,
      payload,
    );
  }
  const body = payload as {
    message?: string;
    data?: { user?: AdminUser };
  };
  if (body.data?.user) {
    setStoredUser(body.data.user);
  }
  return body.message ?? "رمز عبور با موفقیت تغییر کرد.";
}

export async function setSessionContextApi(assignmentId: number, academicYearId: number) {
  return apiPost<{
    active_role: string;
    active_role_label: string;
    active_branch_id: number | null;
    active_branch_name: string | null;
    active_academic_year_id: number;
    assignment_id: number;
    permissions: string[];
    mode: string;
  }>("/context/session", {
    assignment_id: assignmentId,
    academic_year_id: academicYearId,
  });
}

export async function fetchSessionOptions() {
  return apiGet<{
    assignments: Array<{
      id: number;
      role_name: string;
      role_label: string;
      branch_id: number | null;
      branch_name: string | null;
    }>;
    academic_years: Array<{ id: number; title: string; is_current: boolean }>;
  }>("/context/session");
}

export async function fetchAcademicContext(): Promise<AcademicContextState> {
  const data = await apiGet<{
    academic_years: AcademicContextState["academic_years"];
    selected_academic_year_id: number;
    mode: string;
    can_write: boolean;
  }>("/context/academic-year");

  const ctx: AcademicContextState = {
    active_academic_year_id: data.selected_academic_year_id,
    academic_years: data.academic_years,
    mode: data.mode,
  };
  setAcademicContext(ctx);
  return ctx;
}

export async function switchAcademicYear(academicYearId: number) {
  const data = await apiPost<{
    academic_year_id: number;
    mode: string;
    can_write: boolean;
  }>("/context/academic-year", { academic_year_id: academicYearId });

  setAcademicContext({
    active_academic_year_id: data.academic_year_id,
    academic_years: getAcademicYears(),
    mode: data.mode,
  });

  return data;
}

export { getAcademicYearId, getAcademicYears, getAcademicMode, isReadOnlyYear } from "./academic-context";
export type { AcademicContextState };

export interface AdminUser {
  id: number;
  name: string;
  email?: string | null;
  national_code?: string | null;
  organization_id: number;
  roles: string[];
  permissions: string[];
  avatar_path?: string | null;
  password_change_required?: boolean;
  password_change_reasons?: string[];
  role_assignments?: RoleAssignment[];
  branches: Array<{ id: number; name: string; code: string; is_primary: boolean }>;
}

export interface RoleAssignment {
  id: number;
  role_id: number;
  role_name: string;
  role_label: string;
  branch_id: number | null;
  branch_name: string | null;
  branch_code?: string | null;
  permissions?: string[];
}

export interface Paginated<T> {
  current_page: number;
  data: T[];
  last_page: number;
  per_page: number;
  total: number;
}

export interface DeskApplication {
  id: number;
  uuid: string;
  status: string;
  submitted_at?: string | null;
  updated_at?: string | null;
  applicant_national_code: string;
  student_name?: string;
  gender?: "male" | "female" | null;
  birth_date?: string | null;
  branch?: { id: number; name: string };
  grade_level?: { id: number; name: string };
  parents_summary?: {
    father_name?: string | null;
    father_mobile?: string | null;
    mother_name?: string | null;
    mother_mobile?: string | null;
  };
  residence_summary?: string | null;
  documents_count?: number;
  payment_summary?: {
    method?: string;
    amount?: number;
    status?: string;
  } | null;
  document_review_status?: string | null;
  form_data?: Record<string, unknown>;
}

export interface PreRegistrationDashboardData {
  stats: Record<string, number>;
  bucket_labels: Record<string, string>;
  recent: DeskApplication[];
}

export interface InboxNotification {
  id: number;
  type: string;
  title: string;
  body: string;
  meta?: Record<string, unknown>;
  read_at?: string | null;
  created_at?: string | null;
}

export interface RuleSetItem {
  id: number;
  organization_id: number;
  branch_id?: number | null;
  target_type: string;
  target_id: number;
  academic_year_id: number;
  inherits: boolean;
  is_active: boolean;
  rules: Record<string, unknown>;
}

export type {
  Branding,
  GradeLevel,
  EducationStage,
  AcademicYear,
  BranchItem,
  SeatQuotaItem,
  ApplicationDetail,
  SubjectRow,
  StudyFieldOption,
  SubjectFieldCoefficientRow,
} from "./types";
