<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use App\Models\Park;
use Illuminate\Support\Carbon;

class RegularContractController
{
    /**
     * 利用者分類選択肢（user_categoryid 昇順）
     */
    private function buildUsertypeOptions(): array
    {
        return DB::table('usertype as t')
            ->join('regular_contract as rc', 'rc.user_categoryid', '=', 't.user_categoryid')
            ->select('t.user_categoryid', 't.usertype_subject1', 't.usertype_subject2', 't.usertype_subject3')
            ->groupBy('t.user_categoryid', 't.usertype_subject1', 't.usertype_subject2', 't.usertype_subject3')
            ->orderBy('t.user_categoryid', 'asc')
            ->get()
            ->mapWithKeys(function ($row) {
                $label = collect([
                    $row->usertype_subject1 ?? '',
                    $row->usertype_subject2 ?? '',
                    $row->usertype_subject3 ?? '',
                ])->filter(fn ($v) => $v !== '')->implode('/');

                return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
            })
            ->toArray();
    }

    private function buildParkOptions(): array
    {
        return Park::query()
            ->join('regular_contract as rc', 'rc.park_id', '=', 'park.park_id')
            ->select('park.park_id', 'park.park_name')
            ->groupBy('park.park_id', 'park.park_name')
            ->orderBy('park.park_id', 'asc')
            ->get()
            ->mapWithKeys(fn ($park) => [
                $park->park_id => $park->park_name ?: (string) $park->park_id,
            ])
            ->toArray();
    }

    /**
     * datetime-local から受け取った値を Y-m-d H:i:s へ正規化
     */
    private function normalizeDateTimeInput(?string $value, bool $endOfMinute = false): ?string
    {
        if ($value === null) {
            return null;
        }
        $value = trim($value);
        if ($value === '') {
            return null;
        }
        $value = str_replace('T', ' ', $value);
        if (strlen($value) === 16) {
            $value .= ':00';
        }
        try {
            $dt = Carbon::parse($value);
            if ($endOfMinute) {
                $dt = $dt->endOfMinute();
            }
            return $dt->format('Y-m-d H:i:s');
        } catch (\Throwable $e) {
            return null;
        }
    }

    /**
     * 名寄フリガナ検索用：全角カナへ統一し空白除去
     */
    private function normalizePhoneticKeyword(?string $value): ?string
    {
        if ($value === null) {
            return null;
        }
        $value = trim((string) $value);
        if ($value === '') {
            return null;
        }
        $value = mb_convert_kana($value, 'KVCS');
        return str_replace([' ', '　'], '', $value);
    }

    /**
     * 定期契約一覧
     * - ベース表: regular_contract（rc）
     * - 付加情報: user（u）, usertype（t）, park（p）
     * - 画面変数: $list, $sort, $sort_type（既存に合わせる）
     */
    public function list(Request $request)
    {
        if ($request->isMethod('post')) {
            $postParams = $request->except(['_token']);
            $queryParams = $request->query();

            return redirect()->route('regularcontracts', array_merge($queryParams, $postParams));
        }

        $params = $request->query();

        // ===== ソート（既定: contract_id ASC）=====
        $sort = $params['sort'] ?? 'contract_id';
        $sortType = strtolower($params['sort_type'] ?? 'asc') === 'desc' ? 'desc' : 'asc';

        // ===== 絞り込み（テキスト系）=====
        $contract_qr_id = trim((string) ($params['contract_qr_id'] ?? ''));
        $user_id = trim((string) ($params['user_id'] ?? ''));
        $user_tag_serial = trim((string) ($params['user_tag_serial'] ?? ''));
        $park_id = trim((string) ($params['park_id'] ?? ''));
        $selectedParkId = trim((string) ($params['selected_park_id'] ?? ''));
        $user_phonetic = trim((string) ($params['user_phonetic'] ?? ''));
        $phone = trim((string) ($params['phone'] ?? ''));
        $email = trim((string) ($params['email'] ?? ''));
        $user_categoryid = trim((string) ($params['user_categoryid'] ?? ''));
        $park_name_kw = trim((string) ($params['park_name'] ?? ''));
        $zone_keyword = trim((string) ($params['zone_keyword'] ?? ''));
        $zone_name = trim((string) ($params['zone_name'] ?? ''));
        $merge_phonetic_input = $params['merge_phonetic'] ?? '';
        $merge_phonetic = trim((string) $merge_phonetic_input);
        $merge_phonetic_normalized = $this->normalizePhoneticKeyword($merge_phonetic);
        $has_address = $params['has_address'] ?? '';
        $workRecordFilter = (string) ($params['work_record'] ?? '0');
        if (!in_array($workRecordFilter, ['0', '1', '2'], true)) {
            $workRecordFilter = '0';
        }

        // ===== 絞り込み（日付範囲）=====
        $reserve_from = $params['reserve_date_from'] ?? '';
        $reserve_to = $params['reserve_date_to'] ?? '';
        $created_from = $params['contract_created_from'] ?? '';
        $created_to = $params['contract_created_to'] ?? '';
        $updated_from = $params['contract_updated_from'] ?? '';
        $updated_to = $params['contract_updated_to'] ?? '';
        $canceled_from = $params['contract_canceled_from'] ?? '';
        $canceled_to = $params['contract_canceled_to'] ?? '';
        $receipt_delivery_from = $params['receipt_delivery_from'] ?? '';
        $receipt_delivery_to = $params['receipt_delivery_to'] ?? '';
        $contract_valid_months = $params['contract_valid_months'] ?? '';

        // ===== 列挙（全て/0/1）=====
        $contract_flag = $params['contract_flag'] ?? '';
        $contract_permission = $params['contract_permission'] ?? '';
        $tag_qr_flag = $params['tag_qr_flag'] ?? '';
        $updateFlagFilter = (string) ($params['update_flag'] ?? '0');
        if (!in_array($updateFlagFilter, ['0', '1', '2'], true)) {
            $updateFlagFilter = '0';
        }
        $contract_cancel_flag = $params['contract_cancel_flag'] ?? '';

        // ===== クエリ（結合込み）=====
        $q = DB::table('regular_contract as rc')
            ->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
            ->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
            ->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
            ->leftJoin('zone as z', 'z.zone_id', '=', 'rc.zone_id')
            ->select([
                'rc.*',
                'u.user_seq',
                'u.user_name',
                'u.user_phonetic',
                'u.user_mobile',
                'u.user_homephone',
                'u.user_primemail',
                'u.user_regident_zip',
                'u.user_tag_serial',
                DB::raw('t.print_name as usertype_name'),
                't.usertype_subject1',
                't.usertype_subject2',
                't.usertype_subject3',
                DB::raw('p.park_name as park_name'),
                DB::raw('z.zone_name as zone_name'),
            ]);

        // ===== LIKE / キーワード =====
        if ($contract_qr_id !== '') {
            $q->where('rc.contract_qr_id', 'like', "%{$contract_qr_id}%");
        }
        if ($user_id !== '') {
            $q->where('rc.user_id', 'like', "%{$user_id}%");
        }
        if ($user_tag_serial !== '') {
            $q->where('u.user_tag_serial', 'like', "%{$user_tag_serial}%");
        }
        if ($park_id !== '') {
            $q->where('rc.park_id', (int) $park_id);
        } elseif ($selectedParkId !== '') {
            $q->where('rc.park_id', (int) $selectedParkId);
        }
        if ($user_phonetic !== '') {
            $q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
        }
        if ($email !== '') {
            $q->where('u.user_primemail', 'like', "%{$email}%");
        }
        if ($user_categoryid !== '') {
            $q->where('rc.user_categoryid', (int) $user_categoryid);
        }
        if ($park_name_kw !== '') {
            $q->where('p.park_name', 'like', "%{$park_name_kw}%");
        }
        if ($zone_name !== '') {
            $q->where('z.zone_name', 'like', "%{$zone_name}%");
        }
        if ($merge_phonetic_normalized !== null) {
            $likeKeyword = '%' . $merge_phonetic_normalized . '%';
            $q->whereRaw("REPLACE(REPLACE(IFNULL(rc.chk_user_phonetic, ''), ' ', ''), '　', '') LIKE ?", [$likeKeyword]);
        }
        if ($phone !== '') {
            $q->where(function ($w) use ($phone) {
                $w->where('u.user_mobile', 'like', "%{$phone}%")
                    ->orWhere('u.user_homephone', 'like', "%{$phone}%");
            });
        }
        if ($reserve_from !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_from))) {
            $q->where('rc.reserve_date', '>=', $normalized);
        }
        if ($reserve_to !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_to, true))) {
            $q->where('rc.reserve_date', '<=', $normalized);
        }
        if ($receipt_delivery_from !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_from))) {
            $q->where('rc.contract_payment_day', '>=', $normalized);
        }
        if ($receipt_delivery_to !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_to, true))) {
            $q->where('rc.contract_payment_day', '<=', $normalized);
        }
        if ($zone_keyword !== '') {
            $q->where(function ($w) use ($zone_keyword) {
                $w->where('rc.zone_id', 'like', "%{$zone_keyword}%")
                    ->orWhere('rc.pplace_no', 'like', "%{$zone_keyword}%")
                    ->orWhere('rc.old_contract_id', 'like', "%{$zone_keyword}%");
            });
        }
        if ($workRecordFilter === '1') {
            $q->where(function ($w) {
                $w->whereNull('rc.contract_flag')
                    ->orWhere('rc.contract_flag', '=', 0);
            });
        } elseif ($workRecordFilter === '2') {
            $q->where('rc.contract_flag', '=', 1);
        }
        if ($created_from !== '' && ($normalized = $this->normalizeDateTimeInput($created_from))) {
            $q->where('rc.contract_created_at', '>=', $normalized);
        }
        if ($created_to !== '' && ($normalized = $this->normalizeDateTimeInput($created_to, true))) {
            $q->where('rc.contract_created_at', '<=', $normalized);
        }
        if ($updated_from !== '' && ($normalized = $this->normalizeDateTimeInput($updated_from))) {
            $q->where('rc.contract_updated_at', '>=', $normalized);
        }
        if ($updated_to !== '' && ($normalized = $this->normalizeDateTimeInput($updated_to, true))) {
            $q->where('rc.contract_updated_at', '<=', $normalized);
        }
        if ($canceled_from !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_from))) {
            $q->where('rc.contract_cancelday', '>=', $normalized);
        }
        if ($canceled_to !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_to, true))) {
            $q->where('rc.contract_cancelday', '<=', $normalized);
        }
        if ($contract_valid_months !== '') {
            $q->where('rc.enable_months', (int) $contract_valid_months);
        }
        if ($contract_flag !== '') {
            $q->where('rc.contract_flag', (int) $contract_flag);
        }
        if ($contract_permission !== '') {
            $q->where('rc.contract_permission', (int) $contract_permission);
        }
        if ($tag_qr_flag !== '') {
            $q->where('rc.tag_qr_flag', (int) $tag_qr_flag);
        }
        if ($updateFlagFilter === '1') {
            $q->where('rc.update_flag', '=', 1);
        } elseif ($updateFlagFilter === '2') {
            $q->where(function ($w) {
                $w->whereNull('rc.update_flag')
                    ->orWhere('rc.update_flag', '!=', 1);
            });
        }
        if ($contract_cancel_flag !== '') {
            $q->where('rc.contract_cancel_flag', (int) $contract_cancel_flag);
        }

        // ===== ソート（仮想列は結合側にマッピング）=====
        $sortable = [
            'contract_id', 'contract_qr_id', 'old_contract_id', 'zone_id', 'zone_name', 'pplace_no',
            'contract_periods', 'contract_periode', 'user_id', 'user_categoryid', 'reserve_id', 'park_id',
            'price_parkplaceid', 'user_securitynum', 'reserve_date', 'contract_reserve', 'contract_created_at',
            'contract_updated_at', 'contract_cancelday', 'contract_reduction', 'enable_months', 'printable_date',
            'billing_amount', 'contract_payment_day', 'contract_money', 'refunds', 'contract_flag',
            'contract_permission', 'contract_cancel_flag', 'tag_qr_flag', 'update_flag', 'pplace_allocation_flag',
            'settlement_transaction_id', 'contract_seal_issue', 'storage_company_code', 'share_storage_company_code',
            'ope_id', 'park_position', 'contract_manual', 'contract_notice', 'contract_payment_number',
            'user_name', 'user_phonetic', 'user_mobile', 'user_homephone', 'user_primemail', 'user_regident_zip',
            'usertype_name', 'park_name',
        ];
        if (!in_array($sort, $sortable, true)) {
            $sort = 'contract_id';
        }
        $sortMap = [
            'user_name' => 'u.user_name',
            'user_phonetic' => 'u.user_phonetic',
            'user_mobile' => 'u.user_mobile',
            'user_homephone' => 'u.user_homephone',
            'user_primemail' => 'u.user_primemail',
            'user_regident_zip' => 'u.user_regident_zip',
            'usertype_name' => 't.print_name',
            'park_name' => 'p.park_name',
            'zone_name' => 'z.zone_name',
        ];
        $sortColumn = $sortMap[$sort] ?? ('rc.' . $sort);

        $list = $q->orderBy($sortColumn, $sortType)->paginate(50)->withQueryString();

        // ===== 画面へ（Blade 側が参照するすべての変数を渡す）=====
        return view('admin.regularcontracts.list', [
            'list' => $list,
            'sort' => $sort,
            'sort_type' => $sortType,

            // 入力保持（テキスト）
            'contract_qr_id' => $contract_qr_id,
            'user_id' => $user_id,
            'user_tag_serial' => $user_tag_serial,
            'park_id' => $selectedParkId !== '' ? $selectedParkId : $park_id,
            'user_phonetic' => $user_phonetic,
            'phone' => $phone,
            'email' => $email,
            'user_categoryid' => $user_categoryid,
            'park_name' => $park_name_kw,
            'zone_keyword' => $zone_keyword,
            'zone_name' => $zone_name,
            'merge_phonetic' => $merge_phonetic,
            'has_address' => $has_address,
            'work_record' => $workRecordFilter,

            // 入力保持（日付）
            'reserve_date_from' => $reserve_from,
            'reserve_date_to' => $reserve_to,
            'contract_created_from' => $created_from,
            'contract_created_to' => $created_to,
            'contract_updated_from' => $updated_from,
            'contract_updated_to' => $updated_to,
            'contract_canceled_from' => $canceled_from,
            'contract_canceled_to' => $canceled_to,
            'receipt_delivery_from' => $receipt_delivery_from,
            'receipt_delivery_to' => $receipt_delivery_to,
            'contract_valid_months' => $contract_valid_months,

            // 入力保持（列挙）
            'contract_flag' => $contract_flag,
            'contract_permission' => $contract_permission,
            'tag_qr_flag' => $tag_qr_flag,
            'update_flag' => $updateFlagFilter,
            'contract_cancel_flag' => $contract_cancel_flag,

            'userTypeOptions' => $this->buildUsertypeOptions(),
            'parkOptions' => $this->buildParkOptions(),
        ]);
    }

    /**
     * 定期契約編集（GET: 画面表示 / POST: 更新実行）
     * - 主キー: contract_id
     */
    public function edit(Request $request, $id)
    {
        $id = (int) $id;

        if ($request->isMethod('get')) {
            $row = DB::table('regular_contract')->where('contract_id', $id)->first();
            if (!$row) {
                abort(404);
            }
            return view('admin.regularcontracts.edit', [
                'row' => $row,
                'contract_id'  => $id,
            ]);
        }

        $v = Validator::make($request->all(), [
            'user_id' => ['required', 'integer'],
            'park_id' => ['required', 'integer'],
            // 任意項目
            'contract_qr_id' => ['nullable', 'string', 'max:255'],
            'user_categoryid' => ['nullable', 'integer'],
            'reserve_id' => ['nullable', 'integer'],
            'price_parkplaceid' => ['nullable', 'integer'],
            'user_securitynum' => ['nullable', 'string', 'max:255'],
            'reserve_date' => ['nullable', 'date'],
            'contract_reserve' => ['nullable', 'string', 'max:255'],
            'contract_created_at' => ['nullable', 'date'],
            'contract_updated_at' => ['nullable', 'date'],
            'contract_cancelday' => ['nullable', 'date'],
            'contract_flag' => ['nullable', 'integer'],
            'contract_permission' => ['nullable', 'integer'],
            'contract_cancel_flag' => ['nullable', 'integer'],
            'tag_qr_flag' => ['nullable', 'integer'],
            'park_position' => ['nullable', 'string', 'max:255'],
            'ope_id' => ['nullable', 'integer'],
        ]);

        if ($v->fails()) {
            return back()->withErrors($v)->withInput();
        }

        $data = [
            'contract_qr_id' => $request->input('contract_qr_id'),
            'user_id' => (int) $request->input('user_id'),
            'user_categoryid' => $request->input('user_categoryid'),
            'reserve_id' => $request->input('reserve_id'),
            'park_id' => (int) $request->input('park_id'),
            'price_parkplaceid' => $request->input('price_parkplaceid'),
            'user_securitynum' => $request->input('user_securitynum'),
            'reserve_date' => $request->input('reserve_date'),
            'contract_reserve' => $request->input('contract_reserve'),
            'contract_created_at' => $request->input('contract_created_at'),
            'contract_updated_at' => $request->input('contract_updated_at'),
            'contract_cancelday' => $request->input('contract_cancelday'),
            'contract_flag' => $request->input('contract_flag'),
            'contract_permission' => $request->input('contract_permission'),
            'contract_cancel_flag' => $request->input('contract_cancel_flag'),
            'tag_qr_flag' => $request->input('tag_qr_flag'),
            'park_position' => $request->input('park_position'),
            'ope_id' => $request->input('ope_id'),
            'updated_at' => now(),
        ];

        DB::table('regular_contract')->where('contract_id', $id)->update($data);

        return redirect()->route('regularcontracts')->with('success', '定期契約を更新しました。');
    }

    /**
     * 定期契約削除
     * - 物理削除（必要なら cancel フラグ運用に切替）
     */
    public function delete(Request $request)
    {
        $ids = $request->input('ids', []);
        if (!is_array($ids)) {
            $ids = [$ids];
        }
        $ids = array_values(array_filter(
            array_map('intval', $ids),
            static fn (int $v) => $v > 0
        ));

        if (empty($ids)) {
            return redirect()->route('regularcontracts')
                ->with('error', '削除する定期契約が選択されていません。');
        }

        DB::table('regular_contract')
            ->whereIn('contract_id', $ids)
            ->delete();

        return redirect()->route('regularcontracts')->with('success', '定期契約を削除しました。');
    }

    /**
     * 定期契約インポート（仮実装）
     */
    public function import(Request $request)
    {
        if ($request->isMethod('get')) {
            // GET で来たら一覧へ
            return redirect()->route('regularcontracts');
        }

        // ファイル必須 & 形式チェック
        $request->validate([
            'file' => ['required', 'file', 'mimetypes:text/plain,text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
        ], [], [
            'file' => 'インポートファイル',
        ]);

        $file = $request->file('file');

        // TODO: ここで実際のインポート処理（CSV/XLSXの解析とレコード登録）を書く
        // 例：Storage::putFile('imports', $file); で一旦保存してバッチに回す etc.

        return redirect()->route('regularcontracts')->with('success', 'インポートを受け付けました。');
    }



    /**
     * 定期契約エクスポート（仮実装）
     * - 現時点では何もしません。ルーティング確認用のプレーンテキストを返します。
     * - 後で CSV / Excel 出力処理に置き換えてください。
     */

    public function export(Request $request)
    {
        $params   = $request->all();
        $sort     = $params['sort'] ?? 'contract_id';
        $sortType = strtolower($params['sort_type'] ?? 'asc') === 'desc' ? 'desc' : 'asc';
        $type     = (string) ($request->query('type') ?? '');

        $fileName = match ($type) {
            'smbc' => '定期契約マスタ_SMBC.csv',
            'city' => '定期契約マスタ_役所提出用.csv',
            default => '定期契約マスタ.csv',
        };

        $query = $this->buildListQuery($params);

        $columns = [
            'contract_id' => '契約ID',
            'contract_qr_id' => '定期契約ID',
            'old_contract_id' => '旧定期契約番号',
            'pplace_no' => '車室番号',
            'user_id' => '利用者ID',
            'user_categoryid' => '利用者分類ID',
            'tag_qr_flag' => 'タグ・QR',
            'park_id' => '駐輪場ID',
            'reserve_date' => '予約日時',
            'contract_periods' => '有効期間S',
            'contract_periode' => '有効期間E',
            'price_parkplaceid' => '駐輪場所ID',
            'user_securitynum' => '防犯登録番号',
            'contract_created_at' => '契約日時',
            'contract_updated_at' => '更新可能日',
            'contract_cancelday' => '解約日時',
            'contract_reduction' => '減免措置',
            'enable_months' => '定期有効月数',
            'printable_date' => 'シール印刷可能日',
            'billing_amount' => '請求金額',
            'pplace_allocation_flag' => '車室割り当てフラグ',
            'contract_payment_day' => '授受日時',
            'contract_money' => '授受金額',
            'contract_flag' => '授受フラグ',
            'settlement_transaction_id' => '決済トランザクションID',
            'contract_seal_issue' => 'シール発行数',
            'storage_company_code' => '収納企業コード',
            'share_storage_company_code' => '共有先収納企業コード',
            'accept_number' => '受付番号',
            'update_flag' => '（更新元）契約更新済フラグ',
            'vehicle_type_id' => '車種区分ID',
            'chk_user_phonetic' => 'チェック用_フリガナ',
            'user_regident_zip' => 'チェック用_居住所郵便番号',
            'user_mobile' => 'チェック用_携帯電話番号',
            'user_homephone' => 'チェック用_自宅電話番号',
            'old_member_number' => 'チェック用_旧会員番号',
            'user_name' => '利用者氏名',
            'user_phonetic' => '利用者フリガナ',
            'park_name' => '駐輪場名',
            'zone_name' => 'ゾーン名',
            'usertype_name' => '利用者分類名',
        ];

        $dateColumns = [
            'contract_periods',
            'contract_periode',
            'contract_created_at',
            'contract_updated_at',
            'contract_cancelday',
            'printable_date',
            'contract_payment_day',
            'reserve_date',
        ];

        $rows = $query->orderBy($sort, $sortType)->get();

        $headers = [
            'Content-Type'        => 'text/csv; charset=Shift_JIS',
            'Content-Disposition' => "attachment; filename=\"{$fileName}\"",
        ];

        return response()->streamDownload(function () use ($rows, $columns, $dateColumns) {
            $handle = fopen('php://output', 'w');

            $headerRow = array_map(fn ($label) => mb_convert_encoding($label, 'SJIS-win', 'UTF-8'), array_values($columns));
            fputcsv($handle, $headerRow);

            foreach ($rows as $row) {
                $line = [];
                foreach ($columns as $key => $label) {
                    $value = $row->{$key} ?? '';

                    if (in_array($key, $dateColumns, true) && $value) {
                        try {
                            $value = \Illuminate\Support\Carbon::parse($value)->format(str_contains($key, '_day') || str_contains($key, '_date') ? 'Y-m-d H:i' : 'Y-m-d');
                        } catch (\Throwable $e) {
                            $value = (string) $value;
                        }
                    } elseif ($key === 'tag_qr_flag' && $value !== '') {
                        $value = ((int) $value) === 1 ? 'QR' : 'タグ';
                    } elseif ($key === 'pplace_allocation_flag' && $value !== '') {
                        $value = ((int) $value) === 1 ? '割当済' : '未割当';
                    } elseif ($key === 'contract_flag' && $value !== '') {
                        $value = ((int) $value) === 1 ? '済' : '未';
                    } elseif ($key === 'update_flag' && $value !== '') {
                        $value = ((int) $value) === 1 ? '更新済' : '未更新';
                    }

                    $line[] = mb_convert_encoding((string) $value, 'SJIS-win', 'UTF-8');
                }
                fputcsv($handle, $line);
            }

            fclose($handle);
        }, $fileName, $headers);
    }




    // 追加：新規登録（GET: 画面表示 / POST: 登録実行）
    public function add(Request $request)
    {
        // 画面表示
        if ($request->isMethod('get')) {
            return view('admin.regularcontracts.add');
        }

        // ========= バリデーション =========
        // ※ 必須最小限。その他は任意（nullable）
        $v = Validator::make(
            $request->all(),
            [
                'user_id' => ['required', 'integer'],
                'park_id' => ['required', 'integer'],
                'contract_qr_id' => ['nullable', 'string', 'max:255'],
                'user_categoryid' => ['nullable', 'integer'],
                'reserve_id' => ['nullable', 'integer'],
                'price_parkplaceid' => ['nullable', 'integer'],
                'reserve_date' => ['nullable', 'date'],
                'contract_created_at' => ['nullable', 'date'],
                'contract_cancelday' => ['nullable', 'date'],
                'contract_permission' => ['nullable', 'integer'],
                'contract_cancel_flag' => ['nullable', 'integer'],
                'tag_qr_flag' => ['nullable', 'integer'],
                'update_flag' => ['nullable', 'integer'],
                'park_position' => ['nullable', 'string', 'max:255'],
                'ope_id' => ['nullable', 'integer'],
                // 画面の「定期有効月数」は DB の contract_valid_months に保存する
                'enable_months' => ['nullable', 'integer', 'min:0'],
            ],
            [],
            [
                'user_id' => '利用者ID',
                'park_id' => '駐輪場ID',
                'contract_qr_id' => '定期契約QRID',
                'user_categoryid' => '利用者分類ID',
                'reserve_id' => '定期予約ID',
                'price_parkplaceid' => '駐輪場所ID',
                'reserve_date' => '予約日時',
                'contract_created_at' => '契約日時',
                'contract_cancelday' => '解約日時',
                'contract_permission' => 'シール発行許可',
                'contract_cancel_flag' => '解約フラグ',
                'tag_qr_flag' => 'タグ・QR',
                'update_flag' => '（更新元）契約更新済フラグ',
                'park_position' => '駐輪位置番号',
                'ope_id' => 'オペレータID',
                'enable_months' => '定期有効月数',
            ]
        );

        if ($v->fails()) {
            return back()->withErrors($v)->withInput();
        }

        // ========= 登録データ作成 =========
        // ここでは「regular_contract」テーブルに確実にある列を中心に保存します。
        // 追加したい列があれば、同様にキーを増やして下さい。
        $data = [
            'contract_qr_id' => $request->input('contract_qr_id'),
            'user_id' => (int) $request->input('user_id'),
            'user_categoryid' => $request->input('user_categoryid'),
            'reserve_id' => $request->input('reserve_id'),
            'park_id' => (int) $request->input('park_id'),
            'price_parkplaceid' => $request->input('price_parkplaceid'),
            'reserve_date' => $request->input('reserve_date'),
            'contract_created_at' => $request->input('contract_created_at') ?: now(), // 未指定なら現在時刻
            'contract_cancelday' => $request->input('contract_cancelday'),
            'contract_permission' => $request->input('contract_permission'),
            'contract_cancel_flag' => $request->input('contract_cancel_flag'),
            'tag_qr_flag' => $request->input('tag_qr_flag'),
            'update_flag' => $request->input('update_flag'),
            'park_position' => $request->input('park_position'),
            'ope_id' => $request->input('ope_id'),
            // 画面の enable_months → DB の contract_valid_months
            'contract_valid_months' => $request->input('enable_months'),
            'created_at' => now(),
            'updated_at' => now(),
        ];

        DB::table('regular_contract')->insert($data);

        return redirect()
            ->route('regularcontracts')
            ->with('success', '定期契約を登録しました。');
    }

    private function buildListQuery(array $params)
    {
        $contract_qr_id = trim((string)($params['contract_qr_id'] ?? ''));
        $user_id = trim((string)($params['user_id'] ?? ''));
        $user_tag_serial = trim((string)($params['user_tag_serial'] ?? ''));
        $park_id = trim((string)($params['park_id'] ?? ''));
        $selectedParkId = trim((string)($params['selected_park_id'] ?? ''));
        $user_phonetic = trim((string)($params['user_phonetic'] ?? ''));
        $phone = trim((string)($params['phone'] ?? ''));
        $email = trim((string)($params['email'] ?? ''));
        $user_categoryid = trim((string)($params['user_categoryid'] ?? ''));
        $park_name_kw = trim((string)($params['park_name'] ?? ''));
        $zone_keyword = trim((string)($params['zone_keyword'] ?? ''));
        $zone_name = trim((string)($params['zone_name'] ?? ''));
        $merge_phonetic_input = $params['merge_phonetic'] ?? '';
        $merge_phonetic = trim((string)$merge_phonetic_input);
        $merge_phonetic_normalized = $this->normalizePhoneticKeyword($merge_phonetic);
        $workRecordFilter = (string)($params['work_record'] ?? '0');
        if (!in_array($workRecordFilter, ['0', '1', '2'], true)) {
            $workRecordFilter = '0';
        }
        $reserve_from = $params['reserve_date_from'] ?? '';
        $reserve_to = $params['reserve_date_to'] ?? '';
        $created_from = $params['contract_created_from'] ?? '';
        $created_to = $params['contract_created_to'] ?? '';
        $updated_from = $params['contract_updated_from'] ?? '';
        $updated_to = $params['contract_updated_to'] ?? '';
        $canceled_from = $params['contract_canceled_from'] ?? '';
        $canceled_to = $params['contract_canceled_to'] ?? '';
        $receipt_delivery_from = $params['receipt_delivery_from'] ?? '';
        $receipt_delivery_to = $params['receipt_delivery_to'] ?? '';
        $contract_valid_months = $params['contract_valid_months'] ?? '';
        $contract_flag = $params['contract_flag'] ?? '';
        $contract_permission = $params['contract_permission'] ?? '';
        $tag_qr_flag = $params['tag_qr_flag'] ?? '';
        $updateFlagFilter = (string)($params['update_flag'] ?? '0');
        if (!in_array($updateFlagFilter, ['0', '1', '2'], true)) {
            $updateFlagFilter = '0';
        }
        $contract_cancel_flag = $params['contract_cancel_flag'] ?? '';

        $q = DB::table('regular_contract as rc')
            ->leftJoin('user as u', 'u.user_id', '=', 'rc.user_id')
            ->leftJoin('usertype as t', 't.user_categoryid', '=', 'rc.user_categoryid')
            ->leftJoin('park as p', 'p.park_id', '=', 'rc.park_id')
            ->leftJoin('zone as z', 'z.zone_id', '=', 'rc.zone_id')
            ->select([
                'rc.*',
                'u.user_seq',
                'u.user_name',
                'u.user_phonetic',
                'u.user_mobile',
                'u.user_homephone',
                'u.user_primemail',
                'u.user_regident_zip',
                'u.user_tag_serial',
                DB::raw('t.print_name as usertype_name'),
                't.usertype_subject1',
                't.usertype_subject2',
                't.usertype_subject3',
                DB::raw('p.park_name as park_name'),
                DB::raw('z.zone_name as zone_name'),
            ]);

        if ($contract_qr_id !== '') {
            $q->where('rc.contract_qr_id', 'like', "%{$contract_qr_id}%");
        }
        if ($user_id !== '') {
            $q->where('rc.user_id', 'like', "%{$user_id}%");
        }
        if ($user_tag_serial !== '') {
            $q->where('u.user_tag_serial', 'like', "%{$user_tag_serial}%");
        }
        if ($park_id !== '') {
            $q->where('rc.park_id', (int)$park_id);
        } elseif ($selectedParkId !== '') {
            $q->where('rc.park_id', (int)$selectedParkId);
        }
        if ($user_phonetic !== '') {
            $q->where('u.user_phonetic', 'like', "%{$user_phonetic}%");
        }
        if ($email !== '') {
            $q->where('u.user_primemail', 'like', "%{$email}%");
        }
        if ($user_categoryid !== '') {
            $q->where('rc.user_categoryid', (int)$user_categoryid);
        }
        if ($park_name_kw !== '') {
            $q->where('p.park_name', 'like', "%{$park_name_kw}%");
        }
        if ($zone_name !== '') {
            $q->where('z.zone_name', 'like', "%{$zone_name}%");
        }
        if ($merge_phonetic_normalized !== null) {
            $likeKeyword = '%' . $merge_phonetic_normalized . '%';
            $q->whereRaw("REPLACE(REPLACE(IFNULL(rc.chk_user_phonetic, ''), ' ', ''), '　', '') LIKE ?", [$likeKeyword]);
        }
        if ($phone !== '') {
            $q->where(function ($w) use ($phone) {
                $w->where('u.user_mobile', 'like', "%{$phone}%")
                    ->orWhere('u.user_homephone', 'like', "%{$phone}%");
            });
        }
        if ($reserve_from !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_from))) {
            $q->where('rc.reserve_date', '>=', $normalized);
        }
        if ($reserve_to !== '' && ($normalized = $this->normalizeDateTimeInput($reserve_to, true))) {
            $q->where('rc.reserve_date', '<=', $normalized);
        }
        if ($receipt_delivery_from !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_from))) {
            $q->where('rc.contract_payment_day', '>=', $normalized);
        }
        if ($receipt_delivery_to !== '' && ($normalized = $this->normalizeDateTimeInput($receipt_delivery_to, true))) {
            $q->where('rc.contract_payment_day', '<=', $normalized);
        }
        if ($zone_keyword !== '') {
            $q->where(function ($w) use ($zone_keyword) {
                $w->where('rc.zone_id', 'like', "%{$zone_keyword}%")
                    ->orWhere('rc.pplace_no', 'like', "%{$zone_keyword}%")
                    ->orWhere('rc.old_contract_id', 'like', "%{$zone_keyword}%");
            });
        }
        if ($workRecordFilter === '1') {
            $q->where(function ($w) {
                $w->whereNull('rc.contract_flag')
                    ->orWhere('rc.contract_flag', '=', 0);
            });
        } elseif ($workRecordFilter === '2') {
            $q->where('rc.contract_flag', '=', 1);
        }
        if ($created_from !== '' && ($normalized = $this->normalizeDateTimeInput($created_from))) {
            $q->where('rc.contract_created_at', '>=', $normalized);
        }
        if ($created_to !== '' && ($normalized = $this->normalizeDateTimeInput($created_to, true))) {
            $q->where('rc.contract_created_at', '<=', $normalized);
        }
        if ($updated_from !== '' && ($normalized = $this->normalizeDateTimeInput($updated_from))) {
            $q->where('rc.contract_updated_at', '>=', $normalized);
        }
        if ($updated_to !== '' && ($normalized = $this->normalizeDateTimeInput($updated_to, true))) {
            $q->where('rc.contract_updated_at', '<=', $normalized);
        }
        if ($canceled_from !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_from))) {
            $q->where('rc.contract_cancelday', '>=', $normalized);
        }
        if ($canceled_to !== '' && ($normalized = $this->normalizeDateTimeInput($canceled_to, true))) {
            $q->where('rc.contract_cancelday', '<=', $normalized);
        }
        if ($contract_valid_months !== '') {
            $q->where('rc.enable_months', (int)$contract_valid_months);
        }
        if ($contract_flag !== '') {
            $q->where('rc.contract_flag', (int)$contract_flag);
        }
        if ($contract_permission !== '') {
            $q->where('rc.contract_permission', (int)$contract_permission);
        }
        if ($tag_qr_flag !== '') {
            $q->where('rc.tag_qr_flag', (int)$tag_qr_flag);
        }
        if ($updateFlagFilter === '1') {
            $q->where('rc.update_flag', '=', 1);
        } elseif ($updateFlagFilter === '2') {
            $q->where(function ($w) {
                $w->whereNull('rc.update_flag')
                    ->orWhere('rc.update_flag', '!=', 1);
            });
        }
        if ($contract_cancel_flag !== '') {
            $q->where('rc.contract_cancel_flag', (int)$contract_cancel_flag);
        }

        return $q;
    }
}
