import { SquareClient, SquareEnvironment, SquareError, type Square } from "square";

export type PaymentRequest = {
  amount: number;
  currency: string;
  simulateFailure?: boolean;
  sourceId?: string;
  idempotencyKey?: string;
  referenceId?: string;
  returnUrl?: string;
  cancelUrl?: string;
  buyerEmailAddress?: string;
  buyerPhoneNumber?: string;
  note?: string;
};

export type PaymentResult = {
  status: "PENDING" | "PAID" | "FAILED" | "REFUNDED";
  provider: string;
  redirectUrl?: string;
  rawResponse: Record<string, unknown>;
};

export interface PaymentProvider {
  charge(request: PaymentRequest): Promise<PaymentResult>;
}

function serializeSquareErrors(body: unknown) {
  let parsed = body;

  if (typeof body === "string") {
    try {
      parsed = JSON.parse(body);
    } catch {
      return [];
    }
  }

  if (!parsed || typeof parsed !== "object" || !("errors" in parsed) || !Array.isArray(parsed.errors)) {
    return [];
  }

  return parsed.errors.map((squareError) => {
    if (!squareError || typeof squareError !== "object") return {};
    const errorRecord = squareError as Record<string, unknown>;
    return {
      category: typeof errorRecord.category === "string" ? errorRecord.category : undefined,
      code: typeof errorRecord.code === "string" ? errorRecord.code : undefined,
      detail: typeof errorRecord.detail === "string" ? errorRecord.detail : undefined,
      field: typeof errorRecord.field === "string" ? errorRecord.field : undefined
    };
  });
}

function serializeSquareError(error: SquareError) {
  return {
    name: error.name,
    statusCode: error.statusCode,
    message: "Square payment request failed.",
    errors: serializeSquareErrors(error.body)
  };
}

function normalizeSquareStatus(status?: string): PaymentResult["status"] {
  if (status === "COMPLETED" || status === "APPROVED") return "PAID";
  if (status === "PENDING") return "PENDING";
  if (status === "FAILED" || status === "CANCELED") return "FAILED";
  return "PENDING";
}

function paypalEnvironment() {
  return process.env.PAYPAL_ENVIRONMENT?.trim() === "production" ? "production" : "sandbox";
}

function paypalBaseUrl() {
  return paypalEnvironment() === "production" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com";
}

function paypalCredentials() {
  const clientId = process.env.PAYPAL_CLIENT_ID?.trim();
  const clientSecret = process.env.PAYPAL_CLIENT_SECRET?.trim();
  if (!clientId || !clientSecret) {
    throw new Error("PayPal checkout is temporarily unavailable.");
  }
  return { clientId, clientSecret };
}

async function readPayPalBody(response: Response) {
  const text = await response.text();
  if (!text) return {};
  try {
    return JSON.parse(text) as Record<string, unknown>;
  } catch {
    return { message: text.slice(0, 500) };
  }
}

function serializePayPalError(statusCode: number, body: Record<string, unknown>) {
  return {
    statusCode,
    name: typeof body.name === "string" ? body.name : undefined,
    message: typeof body.message === "string" ? body.message : "PayPal payment request failed.",
    debugId: typeof body.debug_id === "string" ? body.debug_id : undefined,
    details: Array.isArray(body.details)
      ? body.details.map((detail) => {
          if (!detail || typeof detail !== "object") return {};
          const detailRecord = detail as Record<string, unknown>;
          return {
            issue: typeof detailRecord.issue === "string" ? detailRecord.issue : undefined,
            description: typeof detailRecord.description === "string" ? detailRecord.description : undefined,
            field: typeof detailRecord.field === "string" ? detailRecord.field : undefined
          };
        })
      : undefined
  };
}

function normalizePayPalStatus(status?: string): PaymentResult["status"] {
  if (status === "COMPLETED") return "PAID";
  if (status === "APPROVED" || status === "CREATED" || status === "PAYER_ACTION_REQUIRED") return "PENDING";
  if (status === "VOIDED" || status === "FAILED" || status === "CANCELLED") return "FAILED";
  return "PENDING";
}

async function getPayPalAccessToken() {
  const { clientId, clientSecret } = paypalCredentials();
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
  const response = await fetch(`${paypalBaseUrl()}/v1/oauth2/token`, {
    method: "POST",
    headers: {
      Authorization: `Basic ${credentials}`,
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: "grant_type=client_credentials"
  });
  const body = await readPayPalBody(response);

  if (!response.ok || typeof body.access_token !== "string") {
    return {
      ok: false as const,
      error: serializePayPalError(response.status, body)
    };
  }

  return { ok: true as const, accessToken: body.access_token };
}

function paypalOrderSummary(body: Record<string, unknown>) {
  const links = Array.isArray(body.links) ? body.links : [];
  const linkSummaries = links
    .map((link) => {
      if (!link || typeof link !== "object") return null;
    const linkRecord = link as Record<string, unknown>;
      const href = typeof linkRecord.href === "string" ? linkRecord.href : "";
      let parsedUrl: URL | null = null;
      try {
        parsedUrl = href ? new URL(href) : null;
      } catch {
        parsedUrl = null;
      }

      return {
        rel: typeof linkRecord.rel === "string" ? linkRecord.rel : undefined,
        method: typeof linkRecord.method === "string" ? linkRecord.method : undefined,
        host: parsedUrl?.host,
        path: parsedUrl?.pathname
      };
    })
    .filter(Boolean) as Array<{ rel?: string; method?: string; host?: string; path?: string }>;

  const approvalRelPriority = ["payer-action", "approve"];
  const approvalLink = links.find((link) => {
    if (!link || typeof link !== "object") return false;
    const linkRecord = link as Record<string, unknown>;
    const rel = typeof linkRecord.rel === "string" ? linkRecord.rel.toLowerCase() : "";
    return approvalRelPriority.includes(rel) && typeof linkRecord.href === "string";
  }) as Record<string, unknown> | undefined;

  let approvalUrl: URL | null = null;
  if (typeof approvalLink?.href === "string") {
    try {
      approvalUrl = new URL(approvalLink.href);
    } catch {
      approvalUrl = null;
    }
  }

  return {
    id: typeof body.id === "string" ? body.id : undefined,
    status: typeof body.status === "string" ? body.status : undefined,
    linkRels: linkSummaries.map((link) => link.rel).filter(Boolean),
    selectedApprovalRel: typeof approvalLink?.rel === "string" ? approvalLink.rel : undefined,
    approvalUrlHost: approvalUrl?.host,
    approvalUrlPath: approvalUrl?.pathname,
    approvalUrl: typeof approvalLink?.href === "string" ? approvalLink.href : undefined
  };
}

function paypalCaptureSummary(body: Record<string, unknown>) {
  const purchaseUnits = Array.isArray(body.purchase_units) ? body.purchase_units : [];
  const firstUnit = purchaseUnits[0] && typeof purchaseUnits[0] === "object" ? purchaseUnits[0] as Record<string, unknown> : {};
  const payments = firstUnit.payments && typeof firstUnit.payments === "object" ? firstUnit.payments as Record<string, unknown> : {};
  const captures = Array.isArray(payments.captures) ? payments.captures : [];
  const firstCapture = captures[0] && typeof captures[0] === "object" ? captures[0] as Record<string, unknown> : {};
  const amount = firstCapture.amount && typeof firstCapture.amount === "object" ? firstCapture.amount as Record<string, unknown> : {};
  const payer = body.payer && typeof body.payer === "object" ? body.payer as Record<string, unknown> : {};

  return {
    id: typeof body.id === "string" ? body.id : undefined,
    status: typeof body.status === "string" ? body.status : undefined,
    capture: {
      id: typeof firstCapture.id === "string" ? firstCapture.id : undefined,
      status: typeof firstCapture.status === "string" ? firstCapture.status : undefined,
      amount: typeof amount.value === "string" ? amount.value : undefined,
      currency: typeof amount.currency_code === "string" ? amount.currency_code : undefined
    },
    payer: {
      email: typeof payer.email_address === "string" ? payer.email_address : undefined
    }
  };
}

export class MockPaymentProvider implements PaymentProvider {
  async charge(request: PaymentRequest): Promise<PaymentResult> {
    const failed = Boolean(request.simulateFailure);
    return {
      status: failed ? "FAILED" : "PAID",
      provider: "MOCK",
      rawResponse: {
        id: `mock_${Date.now()}`,
        amount: request.amount,
        currency: request.currency,
        message: failed ? "Simulated payment failure" : "Simulated payment approval"
      }
    };
  }
}

export class PayPalProvider implements PaymentProvider {
  async charge(request: PaymentRequest): Promise<PaymentResult> {
    const environment = paypalEnvironment();
    const token = await getPayPalAccessToken();
    if (!token.ok) {
      return {
        status: "FAILED",
        provider: "PAYPAL",
        rawResponse: { environment, error: token.error }
      };
    }

    if (!request.returnUrl || !request.cancelUrl) {
      throw new Error("PayPal checkout could not be initialized.");
    }

    const response = await fetch(`${paypalBaseUrl()}/v2/checkout/orders`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token.accessToken}`,
        "Content-Type": "application/json",
        "PayPal-Request-Id": request.idempotencyKey || crypto.randomUUID()
      },
      body: JSON.stringify({
        intent: "CAPTURE",
        purchase_units: [
          {
            reference_id: request.referenceId,
            custom_id: request.referenceId,
            description: request.note,
            amount: {
              currency_code: request.currency,
              value: request.amount.toFixed(2)
            }
          }
        ],
        payment_source: {
          paypal: {
            experience_context: {
              brand_name: "ELIT Gold & Silver Exchange",
              shipping_preference: "NO_SHIPPING",
              user_action: "PAY_NOW",
              return_url: request.returnUrl,
              cancel_url: request.cancelUrl
            }
          }
        }
      })
    });
    const body = await readPayPalBody(response);

    if (!response.ok) {
      return {
        status: "FAILED",
        provider: "PAYPAL",
        rawResponse: { environment, error: serializePayPalError(response.status, body) }
      };
    }

    const order = paypalOrderSummary(body);
    return {
      status: normalizePayPalStatus(order.status),
      provider: "PAYPAL",
      redirectUrl: order.approvalUrl,
      rawResponse: {
        environment,
        order: {
          id: order.id,
          status: order.status,
          linkRels: order.linkRels,
          selectedApprovalRel: order.selectedApprovalRel,
          approvalUrlHost: order.approvalUrlHost,
          approvalUrlPath: order.approvalUrlPath
        }
      }
    };
  }
}

export async function capturePayPalOrder(paypalOrderId: string): Promise<PaymentResult> {
  const environment = paypalEnvironment();
  const token = await getPayPalAccessToken();
  if (!token.ok) {
    return {
      status: "FAILED",
      provider: "PAYPAL",
      rawResponse: { environment, paypalOrderId, error: token.error }
    };
  }

  const response = await fetch(`${paypalBaseUrl()}/v2/checkout/orders/${encodeURIComponent(paypalOrderId)}/capture`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token.accessToken}`,
      "Content-Type": "application/json",
      "PayPal-Request-Id": crypto.randomUUID()
    }
  });
  const body = await readPayPalBody(response);

  if (!response.ok) {
    return {
      status: "FAILED",
      provider: "PAYPAL",
      rawResponse: { environment, paypalOrderId, error: serializePayPalError(response.status, body) }
    };
  }

  const capture = paypalCaptureSummary(body);
  const status = normalizePayPalStatus(capture.capture.status || capture.status);

  return {
    status,
    provider: "PAYPAL",
    rawResponse: {
      environment,
      paypalOrderId,
      order: capture
    }
  };
}

export class SquareProvider implements PaymentProvider {
  async charge(request: PaymentRequest): Promise<PaymentResult> {
    const token = process.env.SQUARE_ACCESS_TOKEN;
    const locationId = process.env.SQUARE_LOCATION_ID;
    const environment = process.env.SQUARE_ENVIRONMENT === "production" ? "production" : "sandbox";
    const squareEnvironment = environment === "production" ? SquareEnvironment.Production : SquareEnvironment.Sandbox;

    if (!token || !locationId) {
      throw new Error("Square checkout is temporarily unavailable.");
    }
    if (!request.sourceId) {
      throw new Error("Square payment details were not provided.");
    }

    const client = new SquareClient({ token, environment: squareEnvironment });

    try {
      const response = await client.payments.create({
        sourceId: request.sourceId,
        idempotencyKey: request.idempotencyKey || crypto.randomUUID(),
        amountMoney: {
          amount: BigInt(Math.round(request.amount * 100)),
          currency: request.currency as Square.Currency
        },
        autocomplete: true,
        locationId,
        referenceId: request.referenceId,
        buyerEmailAddress: request.buyerEmailAddress,
        buyerPhoneNumber: request.buyerPhoneNumber,
        note: request.note
      });

      return {
        status: normalizeSquareStatus(response.payment?.status),
        provider: "SQUARE",
        rawResponse: {
          environment,
          errors: response.errors,
          payment: response.payment
            ? {
                id: response.payment.id,
                status: response.payment.status,
                sourceType: response.payment.sourceType,
                orderId: response.payment.orderId,
                receiptNumber: response.payment.receiptNumber,
                cardStatus: response.payment.cardDetails?.status,
                cardBrand: response.payment.cardDetails?.card?.cardBrand,
                last4: response.payment.cardDetails?.card?.last4,
                totalMoney: response.payment.totalMoney
                  ? {
                      amount: response.payment.totalMoney.amount?.toString(),
                      currency: response.payment.totalMoney.currency
                    }
                  : null
              }
            : null
        }
      };
    } catch (error) {
      if (error instanceof SquareError) {
        return {
          status: "FAILED",
          provider: "SQUARE",
          rawResponse: {
            environment,
            error: serializeSquareError(error)
          }
        };
      }
      throw error;
    }
  }
}

export class StripeProvider implements PaymentProvider {
  async charge(): Promise<PaymentResult> {
    return {
      status: "PENDING",
      provider: "STRIPE",
      rawResponse: { todo: "Integrate Stripe Payment Intents with webhook verification and STRIPE_SECRET_KEY." }
    };
  }
}

export function paymentProvider(method: "MOCK" | "STRIPE" | "PAYPAL" | "SQUARE") {
  if (method === "STRIPE") return new StripeProvider();
  if (method === "PAYPAL") return new PayPalProvider();
  if (method === "SQUARE") return new SquareProvider();
  return new MockPaymentProvider();
}
