<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Laravue\Models\User;
use App\OwnerReward;
use App\Payment;
use App\Product;
use App\Traits\SendsTemplateEmail;
use App\UserCard;
use App\UserPaymentHistory;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use App\Order;
use App\Http\Resources\Admin\OrderResource;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Validator;

/**
 * Class OrderController
 *
 * @package App\Http\Controllers
 */
class OrderController extends Controller
{
	use SendsTemplateEmail;

	const ITEM_PER_PAGE = 100;
	
	public function index(Request $request)
	{
		$searchParams = $request->all();
		$list = Order::notDeleted();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		$keyword = Arr::get($searchParams, 'keyword', '');
		$status = Arr::get($searchParams, 'status', '');
		$dateFrom = Arr::get($searchParams, 'date_from', '');
		$dateTo = Arr::get($searchParams, 'date_to', '');
		$paymentDeadlineFilter = Arr::get($searchParams, 'payment_deadline_filter', '');
		$shippingDeadlineFilter = Arr::get($searchParams, 'shipping_deadline_filter', '');

		if (!empty($keyword)) {
			$keyword = str_replace("#", "", $keyword);
			$list->where('code', 'LIKE', '%' . $keyword . '%');
		}

		if ($status != '') {
			$list->where('status', $status);
		}

		if (!empty($dateFrom)) {
			$list->whereDate('created_at', '>=', $dateFrom);
		}
		if (!empty($dateTo)) {
			$list->whereDate('created_at', '<=', $dateTo);
		}
		
		// Payment deadline filter: status=1 (入金待ち) and payment deadline (wear_date - 11 days) within 11 days from today
		if ($paymentDeadlineFilter == '1') {
			$list->where('status', 1);
			$list->whereHas('order_detail', function ($query) {
				$query->whereNotNull('wear_date')
				      ->whereRaw('DATE_SUB(wear_date, INTERVAL 11 DAY) >= CURDATE()')
				      ->whereRaw('DATE_SUB(wear_date, INTERVAL 11 DAY) <= DATE_ADD(CURDATE(), INTERVAL 11 DAY)');
			});
		}

		// Shipping deadline filter: status=2 (発送待ち) and shipping date (wear_date - store_shipping_days) within N days from today
		$storeShippingDays = (int) config('rental_dates.store_shipping_days', 10);
		$shippingDateSql = "DATE_SUB(wear_date, INTERVAL {$storeShippingDays} DAY)";
		if ($shippingDeadlineFilter == '6') {
			$list->where('status', 2);
			$list->whereHas('order_detail', function ($query) use ($shippingDateSql) {
				$query->whereNotNull('wear_date')
				      ->whereRaw("{$shippingDateSql} >= CURDATE()")
				      ->whereRaw("{$shippingDateSql} <= DATE_ADD(CURDATE(), INTERVAL 6 DAY)");
			});
		} elseif ($shippingDeadlineFilter == '3') {
			$list->where('status', 2);
			$list->whereHas('order_detail', function ($query) use ($shippingDateSql) {
				$query->whereNotNull('wear_date')
				      ->whereRaw("{$shippingDateSql} >= CURDATE()")
				      ->whereRaw("{$shippingDateSql} <= DATE_ADD(CURDATE(), INTERVAL 3 DAY)");
			});
		}
		
		$list->orderBy('id', 'DESC');
		
		return OrderResource::collection($list->paginate($limit));
	}
	
	public function all(Request $request)
	{
		$searchParams = $request->all();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		$list = Order::select('id', 'name', 'slug')->notDeleted()->orderBy('id', 'DESC');
		
		return OrderResource::collection($list->paginate($limit));
	}
	
	public function allNested(Request $request)
	{
		$ignoreId = Arr::get($request->all(), 'ignoreId', '');
		$list = Order::select('*')->isPublished();
		
		if ($ignoreId != null && $ignoreId != "") {
			$list = $list->where('id', '!=', $ignoreId);
		}
		
		$list = $list->orderBy('id', 'DESC')->get();
		
		$rs = $this->getChildItems($list, 0);
		
		return response()->json(['data' => $rs], 200);
	}
	
	public function getChildItems($list, $parent = 0)
	{
		$rs = [];
		foreach ($list as $item) {
			if ($item->parent_id == $parent) {
				$cate = ['value' => $item->id, 'label' => $item->name];
				$children = $this->getChildItems($list, $item->id);
				if ($children !== null && count($children) > 0) $cate['children'] = $children;
				$rs[] = $cate;
			}
		}
		
		return $rs;
	}

	public function chargeCancellationFee(Request $request, $id)
	{
		$order = Order::notDeleted()->where('id', $id)->first();
		if (!isset($order)) return response()->json(['errors' => __('messages.order_not_valid')], 404);
		if ((int) $order->status !== Order::STATUS_CANCELLED) {
			return response()->json(['errors' => 'キャンセル済みの注文のみキャンセル料を請求できます。'], 422);
		}

		$validator = Validator::make($request->all(), [
			'amount' => ['required', 'integer', 'min:1', 'max:9999999'],
			'note'   => ['nullable', 'string', 'max:500'],
		]);
		if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 422);

		$user = User::find($order->user_id);
		if (!$user || !$user->payjp_customer_id) {
			return response()->json(['errors' => '登録済みクレジットカードがありません。'], 422);
		}

		$card = UserCard::where('user_id', $user->id)
			->where('is_deleted', 0)
			->where('is_activated', 1)
			->where('is_default', 1)
			->whereIn('payjp_three_d_secure_status', ['verified', 'attempted'])
			->first();
		if (!$card || !$card->payjp_card_id) {
			return response()->json(['errors' => '3Dセキュア認証済みのデフォルトカードがありません。'], 422);
		}

		$amount = (int) $request->input('amount');
		$note = trim((string) $request->input('note', ''));
		$historyNote = 'キャンセル料';
		if ($note !== '') {
			$historyNote .= '：' . $note;
		}

		try {
			\Payjp\Payjp::setApiKey(config('payjp.secret_key'));
			$charge = \Payjp\Charge::create([
				'customer'    => $user->payjp_customer_id,
				'card'        => $card->payjp_card_id,
				'amount'      => $amount,
				'currency'    => 'jpy',
				'description' => "Cancellation fee for Order #{$order->code}",
			]);

			$payment = null;
			DB::transaction(function () use ($order, $user, $charge, $amount, $historyNote, &$payment) {
				$payment = Payment::create([
					'order_id'        => $order->id,
					'code'            => $charge->id,
					'total'           => $charge->amount,
					'amount_captured' => $charge->captured ? $charge->amount : 0,
					'amount_refunded' => $charge->amount_refunded,
					'application_fee' => 0,
					'application_fee_amount' => 0,
					'currency'        => $charge->currency,
					'type'            => $charge->card->brand ?? '',
					'provider'        => $charge->card->brand ?? '',
					'customer'        => $charge->customer,
					'payment_method'  => 'cancellation_fee',
					'brand'           => $charge->card->brand ?? '',
					'funding'         => '',
					'network'         => '',
					'country'         => $charge->card->country ?? '',
					'last4'           => $charge->card->last4 ?? '',
					'exp_month'       => $charge->card->exp_month ?? '',
					'exp_year'        => $charge->card->exp_year ?? '',
					'failure_code'    => $charge->failure_code,
					'failure_message' => $charge->failure_message,
					'content'         => $charge->card->id ?? '',
					'customer_info'   => $historyNote,
					'finished_at'     => date('Y-m-d H:i:s', $charge->created),
					'status'          => $charge->paid ? 1 : 0,
					'created_at'      => now(),
					'updated_at'      => now(),
				]);

				UserPaymentHistory::create([
					'user_id'        => $user->id,
					'type'           => 0,
					'order_id'       => $order->id,
					'payment_id'     => $payment->id,
					'amount'         => $amount,
					'payment_status' => $charge->paid ? 1 : 0,
					'note'           => $historyNote,
					'created_at'     => now(),
					'updated_at'     => now(),
				]);
			});

			Log::info('Cancellation fee charged successfully', [
				'order_id' => $order->id,
				'order_code' => $order->code,
				'amount' => $amount,
				'charge_id' => $charge->id,
			]);

			return response()->json([
				'status' => 'success',
				'payment_id' => $payment ? $payment->id : null,
				'charge_id' => $charge->id,
				'amount' => $amount,
			], 200);
		} catch (\Payjp\Error\Card $e) {
			return response()->json(['errors' => 'カードエラー: ' . $e->getMessage()], 422);
		} catch (\Exception $e) {
			Log::error('Cancellation fee charge failed', [
				'order_id' => $order->id,
				'amount' => $amount,
				'error' => $e->getMessage(),
			]);
			return response()->json(['errors' => 'キャンセル料の請求に失敗しました: ' . $e->getMessage()], 500);
		}
	}
	
	public function available(Request $request)
	{
		
		$ignoreId = Arr::get($request->all(), 'ignoreId', '');
		$list = Order::select('id', 'name')->isPublished();
		
		if ($ignoreId != null && $ignoreId != "") {
			$list->where('id', '!=', $ignoreId);
		}
		
		$list->orderBy('id');
		
		return OrderResource::collection($list->get());
	}
	
	public function show($id = 0)
	{
		$order = Order::notDeleted()->where('id', $id)->first();
		if (!isset($order)) return response()->json(['errors' => __('messages.order_not_valid')], 403);
		
		return new OrderResource($order);
	}
	
	public function store(Request $request)
	{
		return response()->json(null, 204);
	}
	
	public function update(Request $request, $id = 0)
	{
		$order = Order::notDeleted()->where('id', $id)->first();
		if (!isset($order)) return response()->json(['errors' => __('messages.order_not_valid')], 403);
		$params = $request->all();
		
		// If only status is being updated (old behavior)
		$isSimpleStatusUpdate = isset($params['status'])
			&& !isset($params['payment_method'])
			&& !isset($params['payment_status'])
			&& !isset($params['delivery'])
			&& !isset($params['order_detail']);
		if ($isSimpleStatusUpdate) {
			$validator = Validator::make($request->all(), [
				'status'               => ['required', 'integer', 'in:0,1,2,3,4,5,6,7,8,9'],
				'product_order_status' => ['sometimes', 'integer', 'in:0,1'],
			]);
			if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);

			$statusUpdate = [
				'status'       => $params['status'],
				'is_activated' => false,
				'updated_at'   => date('Y-m-d H:i:s'),
			];
			// 発送完了メール用の伝票番号も同時更新可能にする
			if (array_key_exists('tracking_number', $params)) {
				$statusUpdate['tracking_number'] = $params['tracking_number'] ?: null;
			}
			// キャンセル(8)時のみブロックデートを自動削除（レンタル終了時は商品カレンダーから手動解除）
			if ((int)$params['status'] === Order::STATUS_CANCELLED) {
				try {
					if ($this->refundPaidCardOrder($order)) {
						$statusUpdate['payment_status'] = 2;
					}
				} catch (\RuntimeException $e) {
					return response()->json(['errors' => ['refund' => [$e->getMessage()]]], 500);
				}
			}

			$order->update($statusUpdate);

			if ((int)$params['status'] === Order::STATUS_CANCELLED) {
				\App\ProductSchedule::where('order_id', $order->id)->delete();
			}

			// レンタル終了(6)時: OwnerReward作成 + ペイバック確定メール
			if ((int)$params['status'] === Order::STATUS_RENTAL_END) {
				$this->createOwnerRewardIfNeeded($order);
				$this->sendPaybackConfirmedEmail($order);
			}

			// 商品ステータス連動のメール送信
			$this->sendStatusChangeEmails($order, (int)$params['status']);

			return response()->json(null, 204);
		}
		
		// Full order update (new behavior)
		$validator = Validator::make($request->all(), [
			'payment_method'                         => 'sometimes|integer|in:0,1',
			'payment_status'                         => 'sometimes|integer|in:0,1,2',
			'payment_charged_at'                     => 'sometimes|nullable|date',
			'payment_deadline'                       => 'sometimes|nullable|date',
			'status'                                 => 'sometimes|integer|in:0,1,2,3,4,5,6,7,8',
			'tracking_number'                        => 'sometimes|nullable|string|max:100',
			'delivery.first_name'                    => 'sometimes|string|max:255',
			'delivery.last_name'                     => 'sometimes|string|max:255',
			'delivery.furigana_first_name'           => 'sometimes|string|max:255',
			'delivery.furigana_last_name'            => 'sometimes|string|max:255',
			'delivery.post_code'                     => 'sometimes|string|max:20',
			'delivery.district'                      => 'sometimes|string|max:255',
			'delivery.city'                          => 'sometimes|string|max:255',
			'delivery.address'                       => 'sometimes|string|max:500',
			'delivery.phone_number'                  => 'sometimes|string|max:20',
			'delivery.email'                         => 'sometimes|email|max:255',
			'order_detail'                           => 'sometimes|array',
			'order_detail.*.id'                      => 'required|integer|exists:order_details,id',
			'order_detail.*.wear_date'               => 'sometimes|date',
			'order_detail.*.preferred_delivery_time' => 'sometimes|nullable|string|max:50',
			'order_detail.*.height'                  => 'sometimes|nullable|string|max:10',
			'order_detail.*.hip'                     => 'sometimes|nullable|string|max:10',
			'order_detail.*.foot'                    => 'sometimes|nullable|string|max:10',
			'order_detail.*.sleeve'                  => 'sometimes|nullable|string|max:10',
		]);
		
		if ($validator->fails()) {
			return response()->json(['errors' => $validator->errors()], 422);
		}
		
		// Check for payment method change
		$paymentMethodChanged = false;
		$oldPaymentMethod = $order->payment_method;
		$oldStatus = (int) $order->status;
		$newPaymentMethod = isset($params['payment_method']) ? $params['payment_method'] : $oldPaymentMethod;
		
		if (isset($params['payment_method']) && $oldPaymentMethod != $newPaymentMethod) {
			// 入金済みの場合は支払方法変更を拒否（ただし同時に未入金に戻す場合は許可）
			$currentPaymentStatus = (int)$order->payment_status;
			$newPaymentStatus = isset($params['payment_status']) ? (int)$params['payment_status'] : $currentPaymentStatus;
			if ($currentPaymentStatus === 1 && $newPaymentStatus !== 0) {
				return response()->json([
					'errors' => '入金確認済みのため、お支払い方法を変更できません。入金ステータスを「未入金」に戻してから変更してください。'
				], 422);
			}
			$paymentMethodChanged = true;
		}

		// Update order basic fields
		$orderUpdateData = [];
		if (isset($params['payment_method'])) $orderUpdateData['payment_method'] = $params['payment_method'];
		if (isset($params['payment_status'])) $orderUpdateData['payment_status'] = $params['payment_status'];
		if (array_key_exists('payment_deadline', $params)) {
			$orderUpdateData['payment_deadline'] = $params['payment_deadline'] ?: null;
		}
		// 銀行振込時のみ決済日の手動更新を許可（クレカは自動決済日を保護）
		if (array_key_exists('payment_charged_at', $params) && $newPaymentMethod == 1) {
			$orderUpdateData['payment_charged_at'] = $params['payment_charged_at'] ?: null;
		}
		if (isset($params['status'])) $orderUpdateData['status'] = $params['status'];
		if (array_key_exists('tracking_number', $params)) $orderUpdateData['tracking_number'] = $params['tracking_number'] ?: null;
		
		// Generate payment token if changing to credit card
		if ($paymentMethodChanged && $newPaymentMethod == 1) {
			$orderUpdateData['payment_token'] = \Illuminate\Support\Str::random(32);
			$orderUpdateData['payment_token_expires_at'] = now()->addHours(24);
		}
		
		if (!empty($orderUpdateData)) {
			$orderUpdateData['updated_at'] = date('Y-m-d H:i:s');

			// キャンセル(8)時のみブロックデートを自動削除（レンタル終了時は商品カレンダーから手動解除）
			if (isset($params['status']) && (int)$params['status'] === Order::STATUS_CANCELLED) {
				try {
					if ($this->refundPaidCardOrder($order)) {
						$orderUpdateData['payment_status'] = 2;
					}
				} catch (\RuntimeException $e) {
					return response()->json(['errors' => ['refund' => [$e->getMessage()]]], 500);
				}
			}

			$order->update($orderUpdateData);

			if (isset($params['status']) && (int)$params['status'] === Order::STATUS_CANCELLED) {
				\App\ProductSchedule::where('order_id', $order->id)->delete();
			}

			// ステータスが実際に変更された場合はシンプル更新パスと同じ連動処理を行う
			// （注文編集フォームからのステータス変更でもメール・報酬が走るように揃える）
			if (isset($params['status']) && (int)$params['status'] !== $oldStatus) {
				$newStatus = (int)$params['status'];
				if ($newStatus === Order::STATUS_RENTAL_END) {
					$this->createOwnerRewardIfNeeded($order);
					$this->sendPaybackConfirmedEmail($order);
				}
				$this->sendStatusChangeEmails($order, $newStatus);
			}
		}

		// 銀行振込の入金確認時にオーナー報酬レコードを自動作成
		if (isset($params['payment_status']) && (int)$params['payment_status'] === 1) {
			$this->createOwnerRewardIfNeeded($order);
		}

		// 支払方法変更時、OwnerRewardのpayment_methodも連動更新
		if ($paymentMethodChanged) {
			$newRewardPaymentMethod = (int)$newPaymentMethod === 0
				? OwnerReward::PAYMENT_PAYJP
				: OwnerReward::PAYMENT_MANUAL;
			OwnerReward::where('order_id', $order->id)
				->where('transfer_status', OwnerReward::TRANSFER_PENDING)
				->update(['payment_method' => $newRewardPaymentMethod]);
		}

		// Send email if payment method changed
		if ($paymentMethodChanged) {
			$paymentLink = null;

			if ($newPaymentMethod == 0) {
				// Changed to credit card - generate payment link
				$paymentLink = url('/payment/' . $order->payment_token);
			}

			// 宛先は必ず注文者本人（会員登録メール）。お届け先メールへは送らない
			// （決済リンク・注文情報が贈り先など第三者に渡るのを防ぐ）。
			$orderUser = User::find($order->user_id);
			$recipientEmail = $orderUser ? $orderUser->email : null;
			if ($recipientEmail) {
				try {
					\Mail::to($recipientEmail)->send(new \App\Mail\PaymentMethodChangedUser($order, $newPaymentMethod, $paymentLink));
				} catch (\Throwable $e) {
					Log::warning('PaymentMethodChangedUser mail failed', ['order_id' => $order->id, 'error' => $e->getMessage()]);
				}
			} else {
				Log::warning('PaymentMethodChangedUser mail skipped: orderer email unavailable', ['order_id' => $order->id]);
			}
		}
		
		// Update delivery information
		if (isset($params['delivery']) && $order->delivery()->exists()) {
			$delivery = $order->delivery()->first();
			$deliveryData = [];
			
			if (isset($params['delivery']['first_name'])) $deliveryData['first_name'] = $params['delivery']['first_name'];
			if (isset($params['delivery']['last_name'])) $deliveryData['last_name'] = $params['delivery']['last_name'];
			if (isset($params['delivery']['furigana_first_name'])) $deliveryData['furigana_first_name'] = $params['delivery']['furigana_first_name'];
			if (isset($params['delivery']['furigana_last_name'])) $deliveryData['furigana_last_name'] = $params['delivery']['furigana_last_name'];
			if (isset($params['delivery']['post_code'])) $deliveryData['post_code'] = $params['delivery']['post_code'];
			if (isset($params['delivery']['district'])) $deliveryData['district'] = $params['delivery']['district'];
			if (isset($params['delivery']['city'])) $deliveryData['city'] = $params['delivery']['city'];
			if (isset($params['delivery']['address'])) $deliveryData['address'] = $params['delivery']['address'];
			if (isset($params['delivery']['phone_number'])) $deliveryData['phone_number'] = $params['delivery']['phone_number'];
			if (isset($params['delivery']['email'])) $deliveryData['email'] = $params['delivery']['email'];
			
			if (!empty($deliveryData)) {
				$deliveryData['updated_at'] = date('Y-m-d H:i:s');
				$delivery->update($deliveryData);
			}
		}
		
		// Update order details
		if (isset($params['order_detail']) && is_array($params['order_detail'])) {
			foreach ($params['order_detail'] as $itemData) {
				if (isset($itemData['id'])) {
					$orderDetail = \App\OrderDetail::where('id', $itemData['id'])
						->where('order_id', $order->id)
						->first();

					if ($orderDetail) {
						$updateData = [];
						if (isset($itemData['wear_date'])) $updateData['wear_date'] = $itemData['wear_date'];
						if (array_key_exists('preferred_delivery_time', $itemData)) $updateData['preferred_delivery_time'] = $itemData['preferred_delivery_time'];
						if (array_key_exists('height', $itemData)) $updateData['height'] = $itemData['height'];
						if (array_key_exists('hip', $itemData)) $updateData['hip'] = $itemData['hip'];
						if (array_key_exists('foot', $itemData)) $updateData['foot'] = $itemData['foot'];
						if (array_key_exists('sleeve', $itemData)) $updateData['sleeve'] = $itemData['sleeve'];

						if (!empty($updateData)) {
							$updateData['updated_at'] = date('Y-m-d H:i:s');
							$orderDetail->update($updateData);
						}
					}
				}
			}
		}

		// お客様情報（注文者）は users が正であり、注文編集では変更しない。
		// 以前は customer.* を delivery に書き込んでいたが、お客様情報と配送先情報が
		// 同一レコードで上書きし合う不具合の原因だったため撤去（受入テスト 2026-06-08 指摘）。

		return response()->json(['message' => 'Order updated successfully'], 200);
	}
	
	public function destroy($id = 0)
	{
		$order = Order::notDeleted()->where('id', $id)->first();
		if (!isset($order)) return response()->json(['error' => __('messages.cannot_delete_order')], 403);
		
		try {
			$order->update(['is_deleted' => true]);
		} catch (\Exception $ex) {
			return response()->json(['error' => $ex->getMessage()], 403);
		}
		
		return response()->json(null, 204);
	}
	
	public function destroyMultiple(Request $request)
	{
		$validator = Validator::make($request->all(), ['ids' => 'required']);
		if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
		$listIds = $request->get('ids', []);
		$orders = Order::notDeleted()->whereIn('id', $listIds)->get();
		if ($orders->count() <= 0) return response()->json(['error' => __('messages.order_is_invalid')], 403);
		try {
			Order::notDeleted()->whereIn('id', $listIds)->update(['is_deleted' => true]);
		} catch (\Exception $ex) {
			return response()->json(['error' => $ex->getMessage()], 403);
		}
		
		return response()->json(null, 204);
	}
	
	public function activateMultiple(Request $request)
	{
		$validator = Validator::make($request->all(), ['ids' => 'required']);
		if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
		$listIds = $request->get('ids', []);
		$orders = Order::notDeleted()->whereIn('id', $listIds)->get();
		if ($orders->count() <= 0) return response()->json(['error' => __('messages.order_is_invalid')], 403);
		try {
			Order::notDeleted()->whereIn('id', $listIds)->update(['is_activated' => true]);
		} catch (\Exception $ex) {
			return response()->json(['error' => $ex->getMessage()], 403);
		}
		
		return response()->json(null, 204);
	}
	
	public function deactivateMultiple(Request $request)
	{
		$validator = Validator::make($request->all(), ['ids' => 'required']);
		if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
		$listIds = $request->get('ids', []);
		$orders = Order::notDeleted()->whereIn('id', $listIds)->get();
		if ($orders->count() <= 0) return response()->json(['error' => __('messages.order_is_invalid')], 403);
		try {
			Order::notDeleted()->whereIn('id', $listIds)->update(['is_activated' => false]);
		} catch (\Exception $ex) {
			return response()->json(['error' => $ex->getMessage()], 403);
		}
		
		return response()->json(null, 204);
	}
	
	public function position(Request $request)
	{
		if (!$request->has('position')) return response()->json(['errors' => __('messages.data_is_invalid')], 403);
		$newIndexes = $request->position;
		if (is_array($newIndexes)) {
			foreach ($newIndexes as $item) {
				Order::where('id', $item['id'])->update(['position' => $item['order']]);
			}
		}
		
		return response()->json(null, 204);
	}
	
	public function downloadAll(Request $request)
	{
		$searchParams = $request->all();
		$list = Order::notDeleted()->with(['user', 'delivery', 'order_detail.product_owner.owner', 'payment']);
		$keyword = Arr::get($searchParams, 'keyword', '');
		$status = Arr::get($searchParams, 'status', '');
		$dateFrom = Arr::get($searchParams, 'date_from', '');
		$dateTo = Arr::get($searchParams, 'date_to', '');
		$paymentDateFrom = Arr::get($searchParams, 'payment_date_from', '');
		$paymentDateTo = Arr::get($searchParams, 'payment_date_to', '');
		$paymentDeadlineFilter = Arr::get($searchParams, 'payment_deadline_filter', '');
		$shippingDeadlineFilter = Arr::get($searchParams, 'shipping_deadline_filter', '');

		if (!empty($dateFrom)) {
			$list->whereDate('created_at', '>=', $dateFrom);
		}
		if (!empty($dateTo)) {
			$list->whereDate('created_at', '<=', $dateTo);
		}
		if (!empty($paymentDateFrom)) {
			$list->whereNotNull('payment_charged_at')
				->whereDate('payment_charged_at', '>=', $paymentDateFrom);
		}
		if (!empty($paymentDateTo)) {
			$list->whereNotNull('payment_charged_at')
				->whereDate('payment_charged_at', '<=', $paymentDateTo);
		}
		
		if (!empty($keyword)) {
			$keyword = str_replace("#", "", $keyword);
			$list->where('code', 'LIKE', '%' . $keyword . '%');
		}
		
		if ($status != '') {
			$list->where('status', $status);
		}
		
		// Payment deadline filter: status=1 (入金待ち) and payment deadline (wear_date - 11 days) within 11 days from today
		if ($paymentDeadlineFilter == '1') {
			$list->where('status', 1);
			$list->whereHas('order_detail', function ($query) {
				$query->whereNotNull('wear_date')
				      ->whereRaw('DATE_SUB(wear_date, INTERVAL 11 DAY) >= CURDATE()')
				      ->whereRaw('DATE_SUB(wear_date, INTERVAL 11 DAY) <= DATE_ADD(CURDATE(), INTERVAL 11 DAY)');
			});
		}

		// Shipping deadline filter: status=2 (発送待ち) and shipping date (wear_date - store_shipping_days) within N days from today
		$storeShippingDays = (int) config('rental_dates.store_shipping_days', 10);
		$shippingDateSql = "DATE_SUB(wear_date, INTERVAL {$storeShippingDays} DAY)";
		if ($shippingDeadlineFilter == '6') {
			$list->where('status', 2);
			$list->whereHas('order_detail', function ($query) use ($shippingDateSql) {
				$query->whereNotNull('wear_date')
				      ->whereRaw("{$shippingDateSql} >= CURDATE()")
				      ->whereRaw("{$shippingDateSql} <= DATE_ADD(CURDATE(), INTERVAL 6 DAY)");
			});
		} elseif ($shippingDeadlineFilter == '3') {
			$list->where('status', 2);
			$list->whereHas('order_detail', function ($query) use ($shippingDateSql) {
				$query->whereNotNull('wear_date')
				      ->whereRaw("{$shippingDateSql} >= CURDATE()")
				      ->whereRaw("{$shippingDateSql} <= DATE_ADD(CURDATE(), INTERVAL 3 DAY)");
			});
		}
		
		$list->orderBy('id', 'DESC');
		$orders = $list->get();
		
		return OrderResource::collection($orders);
	}

	/**
	 * 管理画面でクレカ決済済み注文をキャンセルした際に PAY.JP 経由で全額返金する。
	 * 課金は PAY.JP（charge id = ch_...）で行われるため返金も PAY.JP で行う。
	 * 返金に失敗した場合は例外を投げ、キャンセル保存前に操作を止める。
	 */
	private function refundPaidCardOrder(Order $order): bool
	{
		if ((int)$order->payment_method !== 0 || (int)$order->payment_status !== 1) {
			return false;
		}

		$payment = \App\Payment::where('order_id', $order->id)
			->where('is_deleted', false)
			->where('status', 1)
			->first();

		if (!$payment || !$payment->code) {
			throw new \RuntimeException('返金対象のPAY.JP決済IDが見つかりません。');
		}

		try {
			\Payjp\Payjp::setApiKey(config('payjp.secret_key'));
			$charge = \Payjp\Charge::retrieve($payment->code);
			$charge->refund();

			$payment->amount_refunded = $payment->total;
			$payment->status = 2; // 返金済み
			$payment->save();

			$order->payment_status = 2; // 返金済み
			$order->save();
			return true;
		} catch (\Throwable $e) {
			Log::error('PAY.JP admin refund failed', [
				'order_id'  => $order->id,
				'charge_id' => $payment->code,
				'error'     => $e->getMessage(),
			]);
			throw new \RuntimeException('PAY.JP返金に失敗しました。キャンセル処理を中止しました。', 0, $e);
		}
	}

	/**
	 * オーナー商品の注文で、OwnerRewardが未作成の場合に手動振込用レコードを作成
	 */
	private function createOwnerRewardIfNeeded(Order $order): void
	{
		// 既にOwnerRewardがある場合はスキップ
		if (OwnerReward::where('order_id', $order->id)->exists()) {
			return;
		}

		$orderDetail = \App\OrderDetail::where('order_id', $order->id)->first();
		if (!$orderDetail || !$orderDetail->product_id) {
			return;
		}

		$product = Product::find($orderDetail->product_id);
		if (!$product || !$product->isOwnerProduct() || !$product->owner_id || !$product->reward_amount) {
			return;
		}

		$rentalAmount = (int) $orderDetail->total_price;
		$rewardAmount = (int) $product->reward_amount;
		$platformFee = $rentalAmount - $rewardAmount;

		// 報酬額がレンタル金額を上回る（platform_fee が負＝赤字払い）設定ミスは作成せず警告。
		if ($platformFee < 0) {
			Log::warning('OwnerReward skipped: reward_amount exceeds rental amount (negative platform_fee)', [
				'order_id'      => $order->id,
				'product_id'    => $product->id,
				'rental_amount' => $rentalAmount,
				'reward_amount' => $rewardAmount,
			]);
			return;
		}

		// payment_method=0(クレカ)の場合はChargeScheduledPaymentsCommandで作成されるのでスキップ
		// ここでは銀行振込等の手動対応分のみ
		$paymentMethod = (int) $order->payment_method === 0
			? OwnerReward::PAYMENT_PAYJP
			: OwnerReward::PAYMENT_MANUAL;

		OwnerReward::create([
			'owner_id' => $product->owner_id,
			'order_id' => $order->id,
			'product_id' => $product->id,
			'rental_amount' => $rentalAmount,
			'reward_amount' => $rewardAmount,
			'platform_fee' => $platformFee,
			'payment_method' => $paymentMethod,
			'transfer_status' => OwnerReward::TRANSFER_PENDING,
		]);

		Log::info("OrderController: OwnerReward作成 order_id={$order->id}, owner_id={$product->owner_id}, reward={$rewardAmount}");
	}

	/**
	 * ペイバック確定メールをオーナーに送信（レンタル終了時）
	 */
	private function sendPaybackConfirmedEmail(Order $order): void
	{
		try {
			$orderDetail = \App\OrderDetail::where('order_id', $order->id)->first();
			if (!$orderDetail) return;

			$product = Product::find($orderDetail->product_id);
			if (!$product || !$product->isOwnerProduct() || !$product->owner_id) return;

			$owner = User::find($product->owner_id);
			if (!$owner) return;

			$this->sendTemplateEmail('email_owner_payback_confirmed', $owner->email, [
				'owner_name' => $owner->full_name ?? $owner->name,
				'product_name' => $product->name ?? '',
				'rental_amount' => number_format($orderDetail->total_price),
				'reward_amount' => number_format($product->reward_amount),
				'transfer_schedule' => 'レンタル終了月の翌月20日',
			]);
		} catch (\Exception $e) {
			Log::warning("OrderController: ペイバック確定メール送信失敗 order_id={$order->id}, error={$e->getMessage()}");
		}
	}

	/**
	 * ステータス変更に応じたメール送信
	 */
	private function sendStatusChangeEmails(Order $order, int $newStatus): void
	{
		try {
			$user = User::find($order->user_id);
			if (!$user) return;

			$vars = $this->buildOrderEmailVars($order, $user);

			// 返却済み(4) → 返却発送通知
			if ($newStatus === Order::STATUS_RETURNED) {
				$this->sendTemplateEmail('email_return_shipped', $user->email, $vars);
			}

			// 発送待ち(2) かつ 銀行振込 → 決済完了通知（銀行振込）
			if ($newStatus === Order::STATUS_PROCESSING && (int) $order->payment_method === 1) {
				$this->sendTemplateEmail('u_payment_bank_completed', $user->email, $vars);
			}

			// 出荷済み(3) → 発送完了通知
			if ($newStatus === Order::STATUS_SHIPPED) {
				$this->sendTemplateEmail('u_shipping_completed', $user->email, $vars);
			}

			// レンタル終了・取引完了(6) → フォローメール
			if ($newStatus === Order::STATUS_RENTAL_END) {
				$this->sendTemplateEmail('u_rental_follow_up', $user->email, $vars);
			}

			// 一時返却中に対応する注文ステータスは存在しないが、
			// 商品のorder_status=5（一時返却中）への変更は ProductController.updateOrderStatus() で処理
		} catch (\Exception $e) {
			Log::warning("OrderController: ステータス変更メール送信失敗 order_id={$order->id}, error={$e->getMessage()}");
		}
	}

	/**
	 * 注文ステータス変更メール用の変数を生成する。
	 * レンタル日程は config('rental_dates') に従う（お届け-3／返却+2／当店到着+3／発送-10）。
	 */
	private function buildOrderEmailVars(Order $order, User $user): array
	{
		$orderDetail = \App\OrderDetail::where('order_id', $order->id)->first();
		$product = ($orderDetail && $orderDetail->product_id) ? Product::find($orderDetail->product_id) : null;
		$delivery = ($order->delivery && count($order->delivery) > 0) ? $order->delivery[0] : null;

		$productDeliveryDays = (int) config('rental_dates.product_delivery_days', 3);
		$storeShippingDays = (int) config('rental_dates.store_shipping_days', 10);
		$returnAfterDays = (int) config('rental_dates.return_after_days', 2);
		$storeArrivalAfterReturnDays = (int) config('rental_dates.store_arrival_after_return_days', 1);
		$defaultRentalNights = (int) config('rental_dates.default_rental_nights', 5);

		$wearDate = ($orderDetail && $orderDetail->wear_date) ? \Carbon\Carbon::parse($orderDetail->wear_date) : null;
		$wearStr = $wearDate ? $wearDate->format('Y/m/d') : '未定';
		$rentalStart = $wearDate ? $wearDate->copy()->subDays($productDeliveryDays) : null;
		$rentalEnd = $rentalStart ? $rentalStart->copy()->addDays($defaultRentalNights) : null;
		$rentalPeriod = ($rentalStart && $rentalEnd) ? $rentalStart->format('Y/m/d') . ' ～ ' . $rentalEnd->format('Y/m/d') : '未定';
		$shippingDate = $wearDate ? $wearDate->copy()->subDays($storeShippingDays)->format('Y/m/d') : '未定';
		$arrivalDate = $wearDate ? $wearDate->copy()->subDays($productDeliveryDays)->format('Y/m/d') : '未定';
		$returnDate = $wearDate ? $wearDate->copy()->addDays($returnAfterDays)->format('Y/m/d') : '未定';
		$storeArrivalDate = $wearDate ? $wearDate->copy()->addDays($returnAfterDays + $storeArrivalAfterReturnDays)->format('Y/m/d') : '未定';

		$productCode = '';
		if ($product && $product->code) {
			$productCode = $product->code;
		} elseif ($orderDetail && $orderDetail->product_code) {
			$productCode = $orderDetail->product_code;
		}

		$yen = function ($v) { return number_format((int) $v) . '円'; };

		$deliveryName = $delivery ? trim(($delivery->last_name ?? '') . ' ' . ($delivery->first_name ?? '')) : ($user->full_name ?? '');
		$deliveryAddress = $delivery ? trim(($delivery->district ?? '') . ($delivery->city ?? '') . ($delivery->address ?? '')) : '';
		$trackingNumber = $order->tracking_number ?: '';
		$trackingUrl = $trackingNumber
			? ('https://toi.kuronekoyamato.co.jp/cgi-bin/tneko?number=' . $trackingNumber)
			: 'https://toi.kuronekoyamato.co.jp/cgi-bin/tneko';
		$deliveryDate = $order->estimated_delivery_date
			? \Carbon\Carbon::parse($order->estimated_delivery_date)->format('Y/m/d')
			: $arrivalDate;

		$fullName = $user->full_name ?? $user->name;

		return [
			'full_name' => $fullName,
			'user_name' => $fullName,
			'order_code' => $order->code,
			'wear_date' => $wearStr,
			'rental_period' => $rentalPeriod,
			'product_code' => $productCode,
			'product_name' => $orderDetail->product_name ?? ($product->name ?? ''),
			'payment_method' => ((int) $order->payment_method === 0) ? 'クレジットカード' : '銀行振り込み',
			'product_price' => $yen($orderDetail->price ?? 0),
			'option_price' => $yen($order->option_price ?? 0),
			'shipping_cost' => $yen($order->shipping_cost ?? 0),
			'total_price' => $yen($order->total_price ?? 0),
			'shipping_date' => $shippingDate,
			'arrival_date' => $arrivalDate,
			'return_date' => $returnDate,
			'store_arrival_date' => $storeArrivalDate,
			// 発送完了メール用
			'tracking_number' => $trackingNumber,
			'tracking_url' => $trackingUrl,
			'delivery_company' => 'ヤマト運輸',
			'delivery_date' => $deliveryDate,
			'delivery_time' => $orderDetail->preferred_delivery_time ?? '指定なし',
			'delivery_post_code' => $delivery->post_code ?? '',
			'delivery_address' => $deliveryAddress,
			'delivery_name' => $deliveryName,
			'delivery_phone' => $delivery->phone_number ?? '',
		];
	}
}
