"use client";

import { type FormEvent, useMemo, useState } from "react";

import { ModalPortal } from "@/app/_components/core/modal-portal";
import { createFeedbackContext } from "@/app/feedback/feedback-context";
import type { PayChannel } from "@/data/schemas/payment";
import { submitFeedback } from "@/lib/feedback";
import { Logger } from "@/utils/logger";

import type { SubscriptionType } from "../subscription-screen.helpers";
import styles from "./subscription-dialog.module.css";

const PAYMENT_ISSUE_REASONS = [
  {
    value: "cardSecurityConcern",
    label: "Concerned about card information security",
  },
  {
    value: "insufficientBalance",
    label: "Insufficient card or wallet balance",
  },
  {
    value: "unsupportedPaymentMethod",
    label: "No supported payment method available",
  },
  { value: "tooExpensive", label: "Too expensive" },
  { value: "other", label: "Other" },
] as const;

type PaymentIssueReason = (typeof PAYMENT_ISSUE_REASONS)[number]["value"];

const OTHER_DETAILS_MIN_LENGTH = 10;
const OTHER_DETAILS_MAX_LENGTH = 2000;
const SUCCESS_MESSAGE = "Thanks. Your payment issue has been submitted.";
const FAILURE_MESSAGE = "Unable to submit. Please try again.";
const log = new Logger("SubscriptionPaymentIssueDialog");

interface ErrorRecord {
  readonly [key: string]: unknown;
}

export interface PaymentIssueSubmitErrorLogDetails {
  code: string;
  message: string;
  causeName?: string;
  causeMessage?: string;
  httpStatus?: number;
  apiCode?: string;
  serverErrorCode?: string;
}

export interface SubscriptionPaymentIssueDialogProps {
  open: boolean;
  subscriptionType: SubscriptionType;
  planId: string | null;
  orderId: string | null;
  payChannel: PayChannel;
  countryCode?: string | null;
  characterId: string;
  onClose: () => void;
  onSubmitted: (message: string) => void;
}

export function SubscriptionPaymentIssueDialog({
  open,
  subscriptionType,
  planId,
  orderId,
  payChannel,
  countryCode,
  characterId,
  onClose,
  onSubmitted,
}: SubscriptionPaymentIssueDialogProps) {
  const [reason, setReason] = useState<PaymentIssueReason | null>(null);
  const [details, setDetails] = useState("");
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [idempotencyKey] = useState(createIdempotencyKey);

  const normalizedDetails = details.trim();
  const detailsError = useMemo(() => {
    if (reason !== "other") return null;
    if (normalizedDetails.length < OTHER_DETAILS_MIN_LENGTH) {
      return `Please describe the issue in at least ${OTHER_DETAILS_MIN_LENGTH} characters.`;
    }
    if (normalizedDetails.length > OTHER_DETAILS_MAX_LENGTH) {
      return `Your description cannot exceed ${OTHER_DETAILS_MAX_LENGTH} characters.`;
    }
    return null;
  }, [normalizedDetails, reason]);
  const canSubmit = reason !== null && detailsError === null && !isSubmitting;

  const handleClose = () => {
    if (isSubmitting) return;
    onClose();
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (!reason || !canSubmit) {
      setErrorMessage(
        detailsError ?? "Please select the payment problem you encountered.",
      );
      return;
    }

    const reasonLabel = PAYMENT_ISSUE_REASONS.find(
      (item) => item.value === reason,
    )?.label;
    if (!reasonLabel) return;

    setIsSubmitting(true);
    setErrorMessage(null);
    const result = await submitFeedback({
      category: "payment",
      content:
        reason === "other"
          ? `Payment issue: Other\nDetails: ${normalizedDetails}`
          : `Payment issue: ${reasonLabel}`,
      context: {
        ...createFeedbackContext(),
        sourcePage: "subscription",
        paymentIssueReason: reason,
        subscriptionType,
        ...(planId ? { planId } : {}),
        ...(orderId ? { orderId } : {}),
        payChannel,
        ...(countryCode ? { countryCode } : {}),
        characterId,
      },
      images: [],
      idempotencyKey,
    });

    if (!result.success) {
      log.error(
        {
          ...getPaymentIssueSubmitErrorLogDetails(result.error),
          paymentIssueReason: reason,
          subscriptionType,
          planId,
          orderId,
          payChannel,
          countryCode,
          characterId,
        },
        "Payment issue feedback submit failed",
      );
      setErrorMessage(FAILURE_MESSAGE);
      setIsSubmitting(false);
      return;
    }

    setIsSubmitting(false);
    onSubmitted(SUCCESS_MESSAGE);
    onClose();
  };

  return (
    <ModalPortal
      open={open}
      onClose={handleClose}
      scrimClassName={styles.scrim}
      panelClassName={styles.panel}
      scrimOpacity={0.56}
      persistent={isSubmitting}
      ariaLabel="What problem did you encounter?"
    >
      <form className={styles.content} onSubmit={handleSubmit}>
        <h2 className={styles.title}>What problem did you encounter?</h2>
        <fieldset className={styles.reasonList} disabled={isSubmitting}>
          <legend className={styles.srOnly}>Payment problem</legend>
          {PAYMENT_ISSUE_REASONS.map((item) => (
            <label key={item.value} className={styles.reasonOption}>
              <span>{item.label}</span>
              <input
                type="radio"
                name="paymentIssueReason"
                value={item.value}
                checked={reason === item.value}
                onChange={() => {
                  setReason(item.value);
                  setErrorMessage(null);
                }}
              />
            </label>
          ))}
        </fieldset>

        {reason === "other" ? (
          <label className={styles.field}>
            <span>Please describe the issue</span>
            <textarea
              value={details}
              minLength={OTHER_DETAILS_MIN_LENGTH}
              maxLength={OTHER_DETAILS_MAX_LENGTH}
              rows={4}
              disabled={isSubmitting}
              placeholder="Please describe the payment problem you encountered."
              onChange={(event) => {
                setDetails(event.target.value);
                setErrorMessage(null);
              }}
            />
          </label>
        ) : null}

        {errorMessage ? (
          <p className={styles.error} role="alert">
            {errorMessage}
          </p>
        ) : null}

        <div className={styles.actions}>
          <button
            type="button"
            className={styles.secondaryButton}
            disabled={isSubmitting}
            onClick={handleClose}
          >
            Cancel
          </button>
          <button
            type="submit"
            className={styles.primaryButton}
            disabled={!canSubmit}
          >
            {isSubmitting ? "Submitting..." : "Submit"}
          </button>
        </div>
      </form>
    </ModalPortal>
  );
}

export function getPaymentIssueSubmitErrorLogDetails(
  error: Error,
): PaymentIssueSubmitErrorLogDetails {
  const errorRecord = toErrorRecord(error);
  const cause = toErrorRecord(errorRecord?.cause);
  const responseDetails = toErrorRecord(cause?.details);
  const detail = toErrorRecord(responseDetails?.detail);
  const causeName = stringValue(cause?.name);
  const causeMessage = stringValue(cause?.message);
  const httpStatus = numberValue(cause?.status);
  const apiCode = stringValue(cause?.code);
  const serverErrorCode =
    stringValue(detail?.errorCode) ??
    stringValue(responseDetails?.errorCode);

  return {
    code: stringValue(errorRecord?.code) ?? "UNKNOWN",
    message: error.message,
    ...(causeName ? { causeName } : {}),
    ...(causeMessage ? { causeMessage } : {}),
    ...(httpStatus !== undefined ? { httpStatus } : {}),
    ...(apiCode ? { apiCode } : {}),
    ...(serverErrorCode ? { serverErrorCode } : {}),
  };
}

function createIdempotencyKey(): string {
  const token =
    typeof crypto !== "undefined" && "randomUUID" in crypto
      ? crypto.randomUUID()
      : `${Date.now()}_${Math.random().toString(36).slice(2)}`;
  return `payment_feedback_${token}`;
}

function toErrorRecord(value: unknown): ErrorRecord | null {
  if (typeof value !== "object" || value === null) return null;
  return value as ErrorRecord;
}

function stringValue(value: unknown): string | undefined {
  return typeof value === "string" && value.length > 0 ? value : undefined;
}

function numberValue(value: unknown): number | undefined {
  return typeof value === "number" && Number.isFinite(value)
    ? value
    : undefined;
}
