defmodule DaProductAppWeb.DisputeController do use DaProductAppWeb, :controller alias DaProductApp.Disputes @doc """ POST /api/v1/merchant/dispute Allows the merchant to raise a dispute for unmatched/failed settlements. """ def create(conn, params) do # Map comment to merchant_comment for compatibility with spec params = if Map.has_key?(params, "comment") do params |> Map.put("merchant_comment", Map.get(params, "comment")) |> Map.delete("comment") else params end case Disputes.create_dispute(params) do {:ok, dispute} -> response = %{ dispute_id: dispute.dispute_id, status: dispute.status, created_at: DateTime.to_iso8601(dispute.inserted_at) } conn |> put_status(:created) |> json(response) {:error, changeset} -> conn |> put_status(:bad_request) |> json(%{ error: "Invalid dispute data", details: format_changeset_errors(changeset) }) end end @doc """ GET /api/v1/merchant/disputes Returns a paginated list of disputes with optional filters. """ def index(conn, params) do disputes = Disputes.list_disputes(params) total = Disputes.count_disputes(params) page = case Integer.parse(Map.get(params, "page", "1")) do {n, ""} when n > 0 -> n _ -> 1 end page_size = case Integer.parse(Map.get(params, "page_size", "10")) do {n, ""} when n > 0 -> n _ -> 10 end response = %{ total: total, page: page, page_size: page_size, disputes: Enum.map(disputes, &format_dispute_summary/1) } json(conn, response) end @doc """ GET /api/v1/merchant/dispute/:dispute_id Returns detailed information about a specific dispute. """ def show(conn, %{"dispute_id" => dispute_id}) do case Disputes.get_dispute_by_dispute_id(dispute_id) do nil -> conn |> put_status(:not_found) |> json(%{error: "Dispute not found"}) dispute -> response = %{ dispute_id: dispute.dispute_id, txn_id: dispute.txn_id, status: dispute.status, reason: dispute.reason, merchant_comment: dispute.merchant_comment, ops_response: dispute.ops_response, contact_email: dispute.contact_email, bank_user_id: dispute.bank_user_id, created_at: DateTime.to_iso8601(dispute.inserted_at), updated_at: DateTime.to_iso8601(dispute.updated_at) } json(conn, response) end end @doc """ GET /api/v1/merchant/dispute/:dispute_id/download Downloads a PDF/CSV report of the dispute and associated transaction data. """ def download(conn, %{"dispute_id" => dispute_id, "format" => format}) do case Disputes.get_dispute_by_dispute_id(dispute_id) do nil -> conn |> put_status(:not_found) |> json(%{error: "Dispute not found"}) dispute -> case format do "csv" -> csv_content = generate_csv_report(dispute) conn |> put_resp_content_type("text/csv") |> put_resp_header("content-disposition", "attachment; filename=\"dispute_#{dispute_id}.csv\"") |> send_resp(200, csv_content) "pdf" -> # For now, return a simple text response for PDF # In a real implementation, you'd use a PDF library pdf_content = generate_text_report(dispute) conn |> put_resp_content_type("application/pdf") |> put_resp_header("content-disposition", "attachment; filename=\"dispute_#{dispute_id}.pdf\"") |> send_resp(200, pdf_content) _ -> conn |> put_status(:bad_request) |> json(%{error: "Invalid format. Use 'csv' or 'pdf'"}) end end end def download(conn, %{"dispute_id" => dispute_id}) do # Default to PDF if no format specified download(conn, %{"dispute_id" => dispute_id, "format" => "pdf"}) end # Private helper functions defp format_dispute_summary(dispute) do %{ dispute_id: dispute.dispute_id, txn_id: dispute.txn_id, status: dispute.status, reason: dispute.reason, bank_user_id: dispute.bank_user_id, created_at: DateTime.to_iso8601(dispute.inserted_at) } end defp format_changeset_errors(changeset) do Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} -> Enum.reduce(opts, msg, fn {key, value}, acc -> String.replace(acc, "%{#{key}}", to_string(value)) end) end) end defp generate_csv_report(dispute) do "Dispute Report\n" <> "==============\n\n" <> "Dispute ID: #{dispute.dispute_id}\n" <> "Transaction ID: #{dispute.txn_id}\n" <> "Bank User ID: #{dispute.bank_user_id || "N/A"}\n" <> "Status: #{dispute.status}\n" <> "Reason: #{dispute.reason}\n" <> "Merchant Comment: #{dispute.merchant_comment || "N/A"}\n" <> "Ops Response: #{dispute.ops_response || "N/A"}\n" <> "Contact Email: #{dispute.contact_email}\n" <> "Created At: #{DateTime.to_iso8601(dispute.inserted_at)}\n" <> "Updated At: #{DateTime.to_iso8601(dispute.updated_at)}\n" end defp generate_text_report(dispute) do "Dispute Report\n" <> "==============\n\n" <> "Dispute ID: #{dispute.dispute_id}\n" <> "Transaction ID: #{dispute.txn_id}\n" <> "Bank User ID: #{dispute.bank_user_id || "N/A"}\n" <> "Status: #{dispute.status}\n" <> "Reason: #{dispute.reason}\n" <> "Merchant Comment: #{dispute.merchant_comment || "N/A"}\n" <> "Operations Response: #{dispute.ops_response || "N/A"}\n" <> "Contact Email: #{dispute.contact_email}\n" <> "Created: #{DateTime.to_iso8601(dispute.inserted_at)}\n" <> "Last Updated: #{DateTime.to_iso8601(dispute.updated_at)}\n" end end