import type {
  CreatePaymentRecurringPaymentRequest,
  ListPaymentRecurringPaymentsResponse,
  ListPaymentSubscriptionCollectionAttemptsResponse,
  PaginatedResponse,
  PaymentRecurringPayment,
  PaymentRecurringPaymentCollectionResponse,
  PaymentRecurringPaymentResponse,
  PaymentRecurringPaymentStatus,
  PaymentSubscriptionCollectionAttempt,
  UpdatePaymentRecurringPaymentRequest,
} from "@sdp/types";
import type { MessageKey, TranslationValues } from "@/i18n/messages";
import type { SdpApiClient } from "@/lib/sdp-api";
import { getPaymentApiError, parsePaymentApiErrorText } from "../payment-api-errors";
import type { FetchResult } from "../payments-page.data";

type Translate = (key: MessageKey, values?: TranslationValues) => string;

export const RECURRING_PAYMENTS_PAGE_SIZE = 100;

export const RECURRING_LIST_DEFAULT_PAGE_SIZE = 25;

export const RECURRING_PAYMENT_STATUSES = [
  "pending_activation",
  "activating",
  "active",
  "updating",
  "canceling",
  "resuming",
  "paused",
  "canceled",
  "expired",
] as const satisfies readonly PaymentRecurringPaymentStatus[];

export type RecurringPaymentAction = "activate" | "collect" | "cancel" | "resume";

export interface RecurringPaymentsListOptions {
  page?: number;
  pageSize?: number;
  status?: PaymentRecurringPaymentStatus | null;
  counterpartyId?: string;
}

export interface RecurringPaymentsListState {
  page: number;
  pageSize: number;
  status: PaymentRecurringPaymentStatus | null;
}

function firstParamValue(value: string | string[] | undefined): string | undefined {
  return Array.isArray(value) ? value[0] : value;
}

function parseListInteger(value: string | undefined, fallback: number): number {
  if (!value || !/^\d+$/.test(value)) {
    return fallback;
  }
  const parsed = Number(value);
  return parsed > 0 ? parsed : fallback;
}

/**
 * Parses the recurring list's server-driven pagination and filter state from
 * route search params, rejecting anything outside the known vocabulary.
 *
 * @param params - The raw route search params.
 * @returns The validated list state.
 */
export function parseRecurringPaymentsListParams(
  params: Record<string, string | string[] | undefined>
): RecurringPaymentsListState {
  const status = firstParamValue(params.status);
  return {
    page: parseListInteger(firstParamValue(params.page), 1),
    pageSize: Math.min(
      parseListInteger(firstParamValue(params.pageSize), RECURRING_LIST_DEFAULT_PAGE_SIZE),
      RECURRING_PAYMENTS_PAGE_SIZE
    ),
    status:
      status && (RECURRING_PAYMENT_STATUSES as readonly string[]).includes(status)
        ? (status as PaymentRecurringPaymentStatus)
        : null,
  };
}

interface ClientRecurringPaymentsListOptions extends RecurringPaymentsListOptions {
  signal?: AbortSignal;
}

type DashboardApiEnvelope<T> = {
  data?: T;
  error?:
    | string
    | {
        message?: string;
      };
  message?: string;
};

export interface RecurringPaymentCollectionAttemptsResult {
  collectionAttempts: PaymentSubscriptionCollectionAttempt[];
  total: number;
}

function setPositiveInteger(query: URLSearchParams, key: string, value: number | undefined) {
  if (typeof value === "number" && Number.isInteger(value) && value > 0) {
    query.set(key, String(value));
  }
}

export function buildRecurringPaymentsQuery(
  options: RecurringPaymentsListOptions = {}
): URLSearchParams {
  const query = new URLSearchParams();
  setPositiveInteger(query, "page", options.page);
  setPositiveInteger(query, "pageSize", options.pageSize);
  if (options.status) {
    query.set("status", options.status);
  }
  if (options.counterpartyId) {
    query.set("counterpartyId", options.counterpartyId);
  }
  return query;
}

async function readDashboardEnvelope<T>(
  response: Response,
  fallback: string,
  t: Translate
): Promise<T> {
  const body = (await response.json().catch(() => ({}))) as DashboardApiEnvelope<T>;
  if (!response.ok) {
    throw new Error(getPaymentApiError(body, `${fallback} (${response.status}).`));
  }
  if (!body.data) {
    throw new Error(t("DashboardPayments.recurring.emptyResponse", { request: fallback }));
  }
  return body.data;
}

export async function listRecurringPayments(
  options: ClientRecurringPaymentsListOptions = {},
  t: Translate
): Promise<ListPaymentRecurringPaymentsResponse> {
  const { signal, ...filters } = options;
  const query = buildRecurringPaymentsQuery({
    page: filters.page ?? 1,
    pageSize: filters.pageSize ?? RECURRING_PAYMENTS_PAGE_SIZE,
    status: filters.status,
    counterpartyId: filters.counterpartyId,
  });
  const response = await fetch(`/api/dashboard/payments/recurring-payments?${query.toString()}`, {
    method: "GET",
    cache: "no-store",
    signal,
  });
  return readDashboardEnvelope<ListPaymentRecurringPaymentsResponse>(
    response,
    t("DashboardPayments.recurring.unableToLoad"),
    t
  );
}

export async function getRecurringPayment(
  recurringPaymentId: string,
  signal: AbortSignal | undefined,
  t: Translate
): Promise<PaymentRecurringPayment> {
  const response = await fetch(
    `/api/dashboard/payments/recurring-payments/${encodeURIComponent(recurringPaymentId)}`,
    {
      method: "GET",
      cache: "no-store",
      signal,
    }
  );
  const data = await readDashboardEnvelope<PaymentRecurringPaymentResponse>(
    response,
    t("DashboardPayments.recurring.unableToLoad"),
    t
  );
  return data.recurringPayment;
}

export async function createRecurringPayment(
  input: CreatePaymentRecurringPaymentRequest,
  signal: AbortSignal | undefined,
  t: Translate
): Promise<PaymentRecurringPayment> {
  const response = await fetch("/api/dashboard/payments/recurring-payments", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(input),
    signal,
  });
  const data = await readDashboardEnvelope<PaymentRecurringPaymentResponse>(
    response,
    t("DashboardPayments.recurring.unableToCreate"),
    t
  );
  return data.recurringPayment;
}

export async function updateRecurringPayment(
  recurringPaymentId: string,
  input: UpdatePaymentRecurringPaymentRequest,
  signal: AbortSignal | undefined,
  t: Translate
): Promise<PaymentRecurringPayment> {
  const response = await fetch(
    `/api/dashboard/payments/recurring-payments/${encodeURIComponent(recurringPaymentId)}`,
    {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(input),
      signal,
    }
  );
  const data = await readDashboardEnvelope<PaymentRecurringPaymentResponse>(
    response,
    t("DashboardPayments.recurring.paymentUpdateFailed"),
    t
  );
  return data.recurringPayment;
}

export async function runRecurringPaymentAction(
  recurringPaymentId: string,
  action: RecurringPaymentAction,
  signal: AbortSignal | undefined,
  t: Translate
): Promise<PaymentRecurringPayment> {
  const response = await fetch(
    `/api/dashboard/payments/recurring-payments/${encodeURIComponent(
      recurringPaymentId
    )}/${action}`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: "{}",
      signal,
    }
  );
  const data = await readDashboardEnvelope<
    PaymentRecurringPaymentResponse | PaymentRecurringPaymentCollectionResponse
  >(response, t("DashboardPayments.recurring.actionFailed"), t);
  return data.recurringPayment;
}

export async function fetchRecurringPayments(
  request: SdpApiClient["request"],
  t: Translate,
  options: RecurringPaymentsListOptions = {}
): Promise<PaginatedResponse<PaymentRecurringPayment>> {
  try {
    const query = buildRecurringPaymentsQuery({
      page: options.page ?? 1,
      pageSize: options.pageSize ?? RECURRING_PAYMENTS_PAGE_SIZE,
      status: options.status,
      counterpartyId: options.counterpartyId,
    });
    const response = await request(`/v1/payments/recurring-payments?${query.toString()}`);
    if (!response.ok) {
      return {
        ok: false,
        data: [],
        total: 0,
        error: parsePaymentApiErrorText(await response.text()),
      };
    }

    const json = (await response.json()) as { data?: ListPaymentRecurringPaymentsResponse };
    return {
      ok: true,
      data: json.data?.recurringPayments ?? [],
      total: json.data?.total ?? 0,
    };
  } catch (error) {
    return {
      ok: false,
      data: [],
      total: 0,
      error: error instanceof Error ? error.message : t("DashboardPayments.recurring.unableToLoad"),
    };
  }
}

export async function fetchRecurringPaymentById(
  request: SdpApiClient["request"],
  recurringPaymentId: string,
  t: Translate
): Promise<FetchResult<PaymentRecurringPayment>> {
  try {
    const response = await request(
      `/v1/payments/recurring-payments/${encodeURIComponent(recurringPaymentId)}`
    );
    if (!response.ok) {
      return {
        ok: false,
        status: response.status,
        error: parsePaymentApiErrorText(await response.text()),
      };
    }

    const json = (await response.json()) as { data?: PaymentRecurringPaymentResponse };
    if (!json.data?.recurringPayment) {
      return {
        ok: false,
        error: t("DashboardPayments.recurring.unableToLoad"),
      };
    }

    return { ok: true, data: json.data.recurringPayment };
  } catch (error) {
    return {
      ok: false,
      error: error instanceof Error ? error.message : t("DashboardPayments.recurring.unableToLoad"),
    };
  }
}

export async function fetchRecurringPaymentCollectionAttempts(
  request: SdpApiClient["request"],
  subscriptionId: string,
  t: Translate
): Promise<FetchResult<RecurringPaymentCollectionAttemptsResult>> {
  try {
    const response = await request(
      `/v1/payments/subscriptions/${encodeURIComponent(
        subscriptionId
      )}/collection-attempts?page=1&pageSize=25`
    );
    if (!response.ok) {
      return {
        ok: false,
        status: response.status,
        error: parsePaymentApiErrorText(await response.text()),
      };
    }

    const json = (await response.json()) as {
      data?: ListPaymentSubscriptionCollectionAttemptsResponse;
    };
    return {
      ok: true,
      data: {
        collectionAttempts: json.data?.collectionAttempts ?? [],
        total: json.data?.total ?? 0,
      },
    };
  } catch (error) {
    return {
      ok: false,
      error:
        error instanceof Error
          ? error.message
          : t("DashboardPayments.recurring.unableToLoadCollectionHistory", { error: "" }),
    };
  }
}
