import { HTTPException } from "hono/http-exception";
import qs from "qs";

function getBaseURL(path = ""): string {
  const BASE_API = process.env.VIEWPOINT_API!;
  return `${BASE_API}${path}`;
}

/**
 * @param path - Resource pathname
 * @param param - Query params
 * @param options - fetch options
 *
 * @returns  API response
 */
async function VPFetcher<TResponse>(
  path: string,
  params = {},
  options = {},
): Promise<TResponse> {
  try {
    const API_KEY = process.env.VIEWPOINT_API_KEY!;
    const mergedOptions = {
      headers: {
        api_key: API_KEY,
        "Content-Type": "application/json",
      },
      ...options,
    };

    const queryStr = qs.stringify(params);
    const requestUrl = `${getBaseURL(
      `${path}${queryStr ? `?${queryStr}` : ""}`,
    )}`;

    const method = (mergedOptions as { method?: string }).method ?? "GET";
    let response: Response;
    try {
      response = await fetch(requestUrl, mergedOptions);
    } catch (fetchError: any) {
      throw fetchError;
    }

    if (!response.ok) {
      const rawBody = await response.text();
      let data: unknown;
      try {
        data = rawBody ? JSON.parse(rawBody) : {};
      } catch {
        data = { message: rawBody };
      }

      // Status stays in the message: callers match on it to tell "not found"
      // from an outage (see internal/viewpoint/member.ts).
      throw new Error(
        `ViewPoint API Error ${response.status}: ${JSON.stringify(data)} (${method} ${requestUrl})`,
      );
    }
    const textData = await response.text();

    if (!textData) {
      return {} as TResponse;
    }

    try {
      return JSON.parse(textData) as TResponse;
    } catch (jsonError) {
      throw new HTTPException(500, { message: "Invalid JSON response" });
    }
  } catch (error: any) {
    if (error instanceof Error) {
      throw error;
    }
    throw new Error(`Server error: ${error?.message || error}`);
  }
}

export async function VPGet<TResponse, TParams>(
  path: string,
  params?: TParams,
): Promise<TResponse> {
  return VPFetcher(path, params ?? {}, {
    method: "GET",
  });
}

export async function VPPost<TResponse, TParams>(
  path: string,
  params?: TParams,
  body = {},
): Promise<TResponse> {
  return VPFetcher(path, params ?? {}, {
    method: "POST",
    body: JSON.stringify(body),
  });
}

export async function VPPatch<TResponse, TParams>(
  path: string,
  params?: TParams,
  body = {},
): Promise<TResponse> {
  return VPFetcher(path, params ?? {}, {
    method: "Patch",
    body: JSON.stringify(body),
  });
}

export async function VPPut<TResponse, TParams>(
  path: string,
  params?: TParams,
  body = {},
): Promise<TResponse> {
  return VPFetcher(path, params ?? {}, {
    method: "PUT",
    body: JSON.stringify(body),
  });
}

export async function VPDelete<TResponse, TParams>(
  path: string,
  params?: TParams,
): Promise<TResponse> {
  return VPFetcher(path, params ?? {}, {
    method: "DELETE",
  });
}

export async function getMemberPoints(
  memberNo: string,
  clubResortID: number,
): Promise<any[]> {
  return VPGet<any[], any>(
    `/Member/${memberNo}/points/entitlements/${clubResortID}`,
  );
}

export async function transferPoints(payload: any): Promise<any> {
  return VPPost<any, any>(`/Points/transfer`, undefined, payload);
}

export async function createMemberLog(
  memberNo: string,
  payload: any,
): Promise<any> {
  return VPPost<any, any>(`/Member/${memberNo}/log`, undefined, payload);
}

export async function searchMemberByEmail(email: string): Promise<any[]> {
  return VPPost<any[], any>(`/Member/search`, undefined, { Email: email });
}

export async function getMemberByNumber(memberNo: string): Promise<any> {
  return VPGet<any, any>(`/Member/${memberNo}`);
}
