import { Response } from "express";
import { CustomRequest } from "../interfaces/auth";
import { STATUS_CODES } from "../constants/statusCodes";
import { GAS_STATION_PAYMENT_CONSTANTS } from "../constants/messages";
import ResponseUtil from "../utils/Response/responseUtils";
import {
  payGasStationBodySchema,
  gasStationIdParamSchema,
  paymentIdParamSchema,
  paymentListSchema,
  idempotencyKeySchema,
} from "../validators/gasStationPaymentValidator";
import {
  payGasStation,
  getPaymentById,
  listPaymentsByPayer,
  listPaymentsByStationOwner,
} from "../services/gasStationPaymentService";

function handlePaymentError(res: Response, err: unknown): void {
  if (!(err instanceof Error)) {
    ResponseUtil.errorResponse(
      res,
      STATUS_CODES.INTERNAL_SERVER_ERROR,
      "An unexpected error occurred",
    );
    return;
  }

  switch (err.message) {
    case "STATION_NOT_FOUND":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.NOT_FOUND,
        GAS_STATION_PAYMENT_CONSTANTS.STATION_NOT_FOUND,
      );
      break;
    case "STATION_UNAVAILABLE":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        GAS_STATION_PAYMENT_CONSTANTS.STATION_UNAVAILABLE,
      );
      break;
    case "CANNOT_PAY_OWN":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        GAS_STATION_PAYMENT_CONSTANTS.CANNOT_PAY_OWN,
      );
      break;
    case "INSUFFICIENT_FUNDS":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        GAS_STATION_PAYMENT_CONSTANTS.INSUFFICIENT_FUNDS,
      );
      break;
    case "NOT_FOUND":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.NOT_FOUND,
        GAS_STATION_PAYMENT_CONSTANTS.NOT_FOUND,
      );
      break;
    case "FORBIDDEN":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.FORBIDDEN,
        GAS_STATION_PAYMENT_CONSTANTS.FORBIDDEN,
      );
      break;
    case "TRIP_NOT_FOUND":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.NOT_FOUND,
        "Trip not found",
      );
      break;
    case "PAYMENT_IN_PROGRESS":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.CONFLICT,
        "A payment with this Idempotency-Key is already in progress",
      );
      break;
    case "PAYMENT_FAILED":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.INTERNAL_SERVER_ERROR,
        "Payment could not be completed. Retry with the same Idempotency-Key to resume, or a new key after a refunded failure.",
      );
      break;
    case "PAYMENT_PREVIOUSLY_FAILED":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        "This payment already failed. Use a new Idempotency-Key to try again.",
      );
      break;
    case "INVALID_AMOUNT":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        "Invalid payment amount",
      );
      break;
    default:
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.INTERNAL_SERVER_ERROR,
        err.message,
      );
  }
}

const PAY_ERRORS = [
  "STATION_NOT_FOUND",
  "STATION_UNAVAILABLE",
  "CANNOT_PAY_OWN",
  "INSUFFICIENT_FUNDS",
  "TRIP_NOT_FOUND",
  "PAYMENT_IN_PROGRESS",
  "PAYMENT_FAILED",
  "PAYMENT_PREVIOUSLY_FAILED",
  "INVALID_AMOUNT",
];

/**
 * POST /api/v1/gas-stations/:gasStationId/pay
 * Header: Idempotency-Key (required)
 */
export const payRegisteredGasStation = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { gasStationId } = await gasStationIdParamSchema.parseAsync(
      req.params,
    );
    const body = await payGasStationBodySchema.parseAsync(req.body ?? {});

    const rawKey =
      (typeof req.headers["idempotency-key"] === "string"
        ? req.headers["idempotency-key"]
        : undefined) ??
      (typeof req.body?.idempotencyKey === "string"
        ? req.body.idempotencyKey
        : undefined);

    if (!rawKey) {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        GAS_STATION_PAYMENT_CONSTANTS.IDEMPOTENCY_REQUIRED,
      );
    }

    const idempotencyKey = await idempotencyKeySchema.parseAsync(rawKey);

    const result = await payGasStation({
      payerId: req.userId!,
      gasStationId,
      amountDollars: body.amount,
      idempotencyKey,
      note: body.note,
      tripId: body.tripId,
    });

    if (result.status === "failed") {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        GAS_STATION_PAYMENT_CONSTANTS.INSUFFICIENT_FUNDS,
      );
    }

    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      result.status === "completed"
        ? GAS_STATION_PAYMENT_CONSTANTS.PAID
        : GAS_STATION_PAYMENT_CONSTANTS.FETCHED,
    );
  } catch (err) {
    if (err instanceof Error && PAY_ERRORS.includes(err.message)) {
      handlePaymentError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};

/**
 * GET /api/v1/payments/:paymentId
 */
export const getGasStationPayment = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { paymentId } = await paymentIdParamSchema.parseAsync(req.params);
    const result = await getPaymentById(paymentId, req.userId!);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      GAS_STATION_PAYMENT_CONSTANTS.FETCHED,
    );
  } catch (err) {
    if (
      err instanceof Error &&
      ["NOT_FOUND", "FORBIDDEN"].includes(err.message)
    ) {
      handlePaymentError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};

/**
 * GET /api/v1/payments — payments made by the logged-in driver
 */
export const listMyGasStationPayments = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { page, limit } = await paymentListSchema.parseAsync(req.query);
    const result = await listPaymentsByPayer(req.userId!, page, limit);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      GAS_STATION_PAYMENT_CONSTANTS.LIST_FETCHED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};

/**
 * GET /api/v1/gas-stations/my-payments — payments received by my station
 */
export const listMyStationReceivedPayments = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { page, limit } = await paymentListSchema.parseAsync(req.query);
    const result = await listPaymentsByStationOwner(req.userId!, page, limit);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      GAS_STATION_PAYMENT_CONSTANTS.LIST_FETCHED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};
