import {
  MemberAccountNotFound,
  NotFoundError,
  throwException,
} from "@/lib/error";
import { logError } from "@/lib/logger";
import { VPGet, VPPost } from "@/viewpoint";
import type {
  AccountContact,
  ManagementCharge,
  MemeberAccount,
} from "@/viewpoint/type";
import { HTTPException } from "hono/http-exception";
import type { VPMemberAccount } from "./types";

// Read-only Viewpoint member layer, ported from Core as-is. Contact
// create/update lives in Core's member app flow and is not needed by the
// console yet, so it is deliberately left out here.

function filterAccount(account_info: VPMemberAccount, email: string) {
  let account: Record<string, unknown> = {};

  const owner = account_info.Owners.find((o) => o.Email === email);
  if (owner) {
    account = { ...owner };
  } else {
    const contact = account_info.Contacts.find((c) => c.Email === email);
    if (contact) {
      account = { ...contact };
    } else {
      return {};
    }
  }
  return {
    account_id: account_info.AccountID,
    member_name: `${(account?.FirstName as string) ?? ""} ${(account?.LastName as string) ?? ""}`,
    account_type: account_info.AccountType,
    status:
      account.IsOwner || account.IsFamily ? account_info.AccountStatus : null,
    contact_id: account.ContactID,
  };
}

export async function getMemberVPAccountsByEmail(email: string) {
  const response: VPMemberAccount[] = await VPPost("/Member/search", null, {
    email,
  });
  if (response.length === 0) {
    return null;
  }
  const accounts = response.map((account) => filterAccount(account, email));
  if (accounts.length === 0) {
    return null;
  }
  return accounts;
}

// NOTE: Account
export async function hasViewPointAccount(email: string) {
  const response: MemeberAccount[] = await VPPost("/Member/search", null, {
    email,
  });
  if (response.length === 0) {
    throw new HTTPException(404, {
      message: "Member doesn't exists with this email",
    });
  }
}

export async function getViewPointAccountOwner(email: string) {
  const response: MemeberAccount[] = await VPPost("/Member/search", null, {
    email,
  });
  if (response.length === 0) {
    throw new MemberAccountNotFound();
  }
  const owner = response[0].Owners.find((e) => e.Email === email);
  if (!owner) {
    throw new MemberAccountNotFound();
  }
  return owner;
}

export async function getViewPointAccountsByEmail(email: string) {
  const response: MemeberAccount[] = await VPPost("/Member/search", null, {
    email,
  });
  if (response.length === 0) {
    throw new HTTPException(404, {
      message: "Member doesn't exists with this email",
    });
  }

  const accounts = response.map((account) => filterAccount(account, email));
  if (accounts.length === 0) {
    throw new NotFoundError();
  }
  return accounts;
}

/**
 * Status carried by a ViewPoint client error.
 *
 * The client throws a plain Error with the status in its message, and
 * `throwException` flattens anything that isn't an HTTPException into a generic
 * 500 — so a missing member and a broken gateway looked identical in the logs.
 */
function viewpointStatus(error: unknown): number | null {
  const message = error instanceof Error ? error.message : String(error);
  const match = message.match(/ViewPoint API Error (\d{3})/);
  return match ? Number(match[1]) : null;
}

export async function getViewPointAccountById(id: string) {
  try {
    const response: MemeberAccount = await VPGet(`/Member/${id}`);
    return response;
  } catch (error) {
    // Log the real cause before it is flattened, then let "not found" be a null
    // rather than a 500 the caller can't distinguish from an outage.
    logError(`Viewpoint /Member/${id} failed: ${String(error)}`);
    if (viewpointStatus(error) === 404) {
      return null;
    }
    throwException(error);
  }
}

export async function getViewPointAccountByContactId(id: number) {
  try {
    const response: AccountContact = await VPGet(`/Contact/${id}`);
    return response;
  } catch (error) {
    logError(`Viewpoint /Contact/${id} failed: ${String(error)}`);
    // A member record without a Viewpoint contact still has DB fields worth
    // showing, so this degrades to "no CRM data" instead of failing the page.
    if (viewpointStatus(error) === 404) {
      return null;
    }
    throwException(error);
  }
}

// NOTE: Management Charges
export async function getManagementCharges(account_id: string) {
  try {
    const response: ManagementCharge[] = await VPGet(
      `/Member/${account_id}/charges`,
    );
    return response;
  } catch (error) {
    throwException(error);
  }
}
