<?php

namespace App\Services;

use App\Models\Park;
use App\Models\EarningsSummary;
use App\Models\Psection;
use App\Models\OperatorQue;
use App\Models\Device;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

/**
 * SHJ-10 売上集計処理サービス
 * 
 * 財政年度ベースの年次・月次売上集計処理を実行するビジネスロジック
 * バッチ処理「SHJ-10売上集計（年次・月次）」の核となる処理を担当
 */
class ShjTenService
{
    /**
     * 財政年度開始月
     *
     * @var int
     */
    const FISCAL_START_MONTH = 4;

    /**
     * バッチ実行者の固定オペレータID
     */
    const BATCH_OPERATOR_ID = 9999999;

    /**
     * 年次集計の summary_type 値
     */
    const SUMMARY_TYPE_YEARLY = 1;

    /**
     * 月次集計の summary_type 値
     */
    const SUMMARY_TYPE_MONTHLY = 2;

    /**
     * ShjEightService インスタンス
     *
     * @var ShjEightService
     */
    protected $shjEightService;

    /**
     * コンストラクタ
     *
     * @param ShjEightService $shjEightService
     */
    public function __construct(ShjEightService $shjEightService)
    {
        $this->shjEightService = $shjEightService;
    }

    /**
     * SHJ-10 財政年度売上集計処理メイン実行
     * 
     * 処理フロー (todo/SHJ-10/SHJ-10.txt):
     * 【処理1】集計対象を設定する
     * 【処理2】駐輪場マスタを取得する
     * 【判断1】取得件数判定
     * 【処理3】車種区分毎に算出する
     * 【判断2】取得判定
     * 【処理4】売上集計結果を削除→登録する
     * 【処理5】バッチ処理ログを作成し、情報不備がある場合のみオペレータキューを作成する
     *
     * @param string $type 集計種別（yearly/monthly）
     * @param string $target 集計対象
     * @param array $fiscalPeriod 財政期間情報
     * @return array 処理結果
     */
    public function executeFiscalEarningsAggregation(string $type, string $target, array $fiscalPeriod): array
    {
        $statusComments = []; // 内部変数.ステータスコメント
        $dataIntegrityIssues = []; // 内部変数.情報不備

        try {
            // 【処理1】集計対象を設定する（財政年度ベース）
            $aggregationTarget = $this->setFiscalAggregationTarget($fiscalPeriod);

            Log::info('SHJ-10 売上集計処理開始', [
                'type' => $type,
                'target' => $target,
                'fiscal_period' => $fiscalPeriod,
                'aggregation_target' => $aggregationTarget
            ]);

            // 【処理2】駐輪場マスタを取得する
            $parkInfo = $this->getParkInformation();

            // 【判断1】取得件数判定
            if (empty($parkInfo)) {
                $typeLabel = $this->getTypeLabel($type);
                $statusComment = "売上集計{$typeLabel}：駐輪場マスタが存在していません。";
                $statusComments[] = $statusComment;

                // 【処理5】オペレータキュー作成
                $this->createOperatorQueue($statusComment, null);

                // SHJ-8 バッチ処理ログ作成
                $this->callShjEight('SHJ-10売上集計（年次・月次）', 'success', $statusComment);

                return [
                    'success' => true,
                    'message' => $statusComment,
                    'processed_parks' => 0,
                    'summary_records' => 0
                ];
            }

            // 【処理3】車種区分毎に算出する & 【処理4】売上集計結果を削除→登録する
            $summaryRecords = 0;
            $processedParks = 0;

            foreach ($parkInfo as $park) {
                $result = $this->processFiscalEarningsForPark($park, $aggregationTarget, $fiscalPeriod);

                $processedParks++;
                $summaryRecords += $result['summary_records'];

                // 対象データなしの場合のステータスコメント収集
                if (!empty($result['no_data_message'])) {
                    $statusComments[] = $result['no_data_message'];
                }

                // 情報不備を収集（"なし"でない場合）
                if ($result['data_integrity_issue'] !== '情報不備：なし') {
                    $dataIntegrityIssues[] = $result['data_integrity_issue'];
                }
            }

            // 最終ステータスコメント生成
            $typeLabel = $this->getTypeLabel($type);
            $finalStatusComment = "売上集計{$typeLabel}：対象={$fiscalPeriod['target_label']}、駐輪場数={$processedParks}、集計レコード数={$summaryRecords}";
            if (!empty($dataIntegrityIssues)) {
                $finalStatusComment .= "、情報不備=" . implode('、', $dataIntegrityIssues);
            }

            // 【処理5】オペレータキュー作成
            // ※ 駐輪場単位で既に作成済み（processFiscalEarningsForPark内で情報不備検出時に実施）
            if (!empty($dataIntegrityIssues)) {
                Log::warning('SHJ-10 情報不備検出', [
                    'issues' => $dataIntegrityIssues
                ]);
            }

            // SHJ-8 バッチ処理ログ作成
            $this->callShjEight('SHJ-10売上集計（年次・月次）', 'success', $finalStatusComment);

            Log::info('SHJ-10 売上集計処理完了', [
                'processed_parks' => $processedParks,
                'summary_records' => $summaryRecords,
                'data_integrity_issues' => count($dataIntegrityIssues),
                'no_data_parks' => count($statusComments)
            ]);

            return [
                'success' => true,
                'message' => 'SHJ-10 売上集計処理が正常に完了しました',
                'processed_parks' => $processedParks,
                'summary_records' => $summaryRecords,
                'data_integrity_issues' => count($dataIntegrityIssues)
            ];

        } catch (\Exception $e) {
            $typeLabel = $this->getTypeLabel($type ?? 'unknown');
            $errorMessage = "売上集計{$typeLabel}：エラー発生 - " . $e->getMessage();

            // SHJ-8 バッチ処理ログ作成（エラー時も作成）
            try {
                $this->callShjEight('SHJ-10売上集計（年次・月次）', 'error', $errorMessage);
            } catch (\Exception $shjException) {
                Log::error('SHJ-8呼び出しエラー', ['error' => $shjException->getMessage()]);
            }

            Log::error('SHJ-10 売上集計処理エラー', [
                'exception' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            return [
                'success' => false,
                'message' => $errorMessage,
                'details' => $e->getMessage()
            ];
        }
    }

    /**
     * 【処理1】財政年度集計対象を設定する
     * 
     * @param array $fiscalPeriod 財政期間情報
     * @return array 集計対象情報
     */
    private function setFiscalAggregationTarget(array $fiscalPeriod): array
    {
        return [
            'type' => $fiscalPeriod['type'],
            'start_date' => $fiscalPeriod['start_date'],
            'end_date' => $fiscalPeriod['end_date'],
            'summary_type' => $fiscalPeriod['summary_type'],
            'fiscal_year' => $fiscalPeriod['fiscal_year'],
            'target_label' => $fiscalPeriod['target_label']
        ];
    }

    /**
     * 【処理2】駐輪場マスタを取得する
     *
     * @return array 駐輪場情報
     */
    private function getParkInformation(): array
    {
        try {
            $parkInfo = DB::table('park')
                ->select(['park_id', 'park_name'])
                ->where('park_close_flag', '<>', 1)
                ->orderBy('park_ruby')
                ->get()
                ->toArray();

            Log::info('駐輪場マスタ取得完了', [
                'park_count' => count($parkInfo)
            ]);

            return $parkInfo;

        } catch (\Exception $e) {
            Log::error('駐輪場マスタ取得エラー', [
                'error' => $e->getMessage()
            ]);

            throw $e;
        }
    }

    /**
     * 駐輪場毎の財政年度売上集計処理
     *
     * @param object $park 駐輪場情報
     * @param array $aggregationTarget 集計対象
     * @param array $fiscalPeriod 財政期間情報
     * @return array 処理結果 ['summary_records' => int, 'data_integrity_issue' => string, 'no_data_message' => string|null]
     */
    private function processFiscalEarningsForPark($park, array $aggregationTarget, array $fiscalPeriod): array
    {
        try {
            $startDate = $aggregationTarget['start_date'];
            $endDate = $aggregationTarget['end_date'];

            // 0. 情報不備チェック
            $dataIntegrityIssue = $this->checkDataIntegrity($park->park_id, $startDate, $endDate);

            // 情報不備がある場合、駐輪場単位でオペレータキュー作成（仕様 todo/SHJ-10/SHJ-10.txt:289-299）
            if ($dataIntegrityIssue !== '情報不備：なし') {
                $this->createOperatorQueue($dataIntegrityIssue, $park->park_id);
            }
            
            // ① 定期契約データ取得（車種区分・分類名１・定期有効月数毎）
            $regularData = $this->calculateRegularEarnings($park->park_id, $startDate, $endDate);
            
            // ② 一時金データ取得（車種毎）
            $lumpsumData = $this->calculateLumpsumEarnings($park->park_id, $startDate, $endDate);
            
            // ③ 解約返戻金データ取得（車種区分毎）
            $refundData = $this->calculateRefundEarnings($park->park_id, $startDate, $endDate);
            
            // ④ 再発行データ取得（車種区分毎）
            $reissueData = $this->calculateReissueCount($park->park_id, $startDate, $endDate);
            
            // 【判断2】データがいずれかあれば【処理4】へ
            if (empty($regularData) && empty($lumpsumData) && empty($refundData) && empty($reissueData)) {
                // 対象データなし - 仕様 todo/SHJ-10/SHJ-10.txt:209-211
                $typeLabel = $this->getTypeLabel($aggregationTarget['type']);
                $noDataMessage = "売上集計{$typeLabel}：{$startDate}～{$endDate}／駐輪場：{$park->park_name}：売上データが存在しません。";
                
                return [
                    'summary_records' => 0,
                    'data_integrity_issue' => $dataIntegrityIssue,
                    'no_data_message' => $noDataMessage
                ];
            }
            
            // 【処理4】既存の売上集計結果を削除
            $this->deleteExistingFiscalSummary($park->park_id, $aggregationTarget);
            
            // 【処理4】売上集計結果を登録
            $summaryRecords = 0;
            
            // ① 定期契約データがある場合：同じ組合せ（psection×usertype×months）を統合
            $mergedRegularData = $this->mergeRegularDataByGroup($regularData);
            foreach ($mergedRegularData as $key => $mergedRow) {
                $this->createFiscalEarningsSummary($park, $mergedRow, $aggregationTarget, $fiscalPeriod, 'regular');
                $summaryRecords++;
            }
            
            // ②③④ 一時金・解約・再発行データがある場合（車種区分毎に集約）
            $otherDataByPsection = $this->mergeOtherEarningsData($lumpsumData, $refundData, $reissueData);
            foreach ($otherDataByPsection as $psectionId => $data) {
                $this->createFiscalEarningsSummary($park, $data, $aggregationTarget, $fiscalPeriod, 'other');
                $summaryRecords++;
            }
            
            Log::info('駐輪場売上集計完了', [
                'park_id' => $park->park_id,
                'park_name' => $park->park_name,
                'summary_records' => $summaryRecords,
                'data_integrity_issue' => $dataIntegrityIssue
            ]);
            
            return [
                'summary_records' => $summaryRecords,
                'data_integrity_issue' => $dataIntegrityIssue,
                'no_data_message' => null
            ];
            
        } catch (\Exception $e) {
            Log::error('駐輪場売上集計エラー', [
                'park_id' => $park->park_id,
                'error' => $e->getMessage()
            ]);
            
            throw $e;
        }
    }

    /**
     * 0. 情報不備チェック（期間集計版）
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:77-101
     * 
     * @param int $parkId 駐輪場ID
     * @param string $startDate 集計開始日（YYYY-MM-DD）
     * @param string $endDate 集計終了日（YYYY-MM-DD）
     * @return string 情報不備メッセージ（仕様フォーマット："情報不備：xxx" or "情報不備：なし"）
     */
    private function checkDataIntegrity(int $parkId, string $startDate, string $endDate): string
    {
        $incompleteContracts = DB::table('regular_contract')
            ->select('contract_id')
            ->where('park_id', $parkId)
            ->where('contract_flag', 1)
            ->whereBetween(DB::raw('DATE(contract_payment_day)'), [$startDate, $endDate])
            ->where(function($query) {
                $query->whereNull('update_flag')
                      ->orWhereNull('psection_id')
                      ->orWhereNull('enable_months');
            })
            ->pluck('contract_id')
            ->toArray();
        
        if (empty($incompleteContracts)) {
            return '情報不備：なし';
        }
        
        // 仕様フォーマット："情報不備：" + 契約IDカンマ区切り
        return '情報不備：' . implode(',', $incompleteContracts);
    }

    /**
     * ① 定期契約データ取得（車種区分・分類名１・定期有効月数毎）
     * ※ 金額・件数は全て0固定だが、分類や月数の組み合わせごとにレコードが必要
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:104-128
     * 
     * @param int $parkId 駐輪場ID
     * @param string $startDate 集計開始日（YYYY-MM-DD）
     * @param string $endDate 集計終了日（YYYY-MM-DD）
     * @return array
     */
    /**
     * ① 定期契約データ取得（車種区分・分類名１・定期有効月数毎）
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:104-129
     * SQL定義：減免措置・継続フラグ・車種区分・分類名・有効月数でグループ化し、
     * 授受金額の合計と件数を算出する
     * 
     * @param int $parkId 駐輪場ID
     * @param string $startDate 集計開始日（YYYY-MM-DD）
     * @param string $endDate 集計終了日（YYYY-MM-DD）
     * @return array
     */
    private function calculateRegularEarnings(int $parkId, string $startDate, string $endDate): array
    {
        $results = DB::table('regular_contract as T1')
            ->join('usertype as T3', 'T1.user_categoryid', '=', 'T3.user_categoryid')
            ->select([
                DB::raw('IFNULL(T1.contract_reduction, 0) as contract_reduction'),
                'T1.update_flag',
                'T1.psection_id',
                'T3.usertype_subject1',
                'T1.enable_months',
                DB::raw('SUM(T1.contract_money) as total_amount'), // 仕様：授受金額の合計
                DB::raw('COUNT(T1.contract_money) as contract_count') // 仕様：授受件数
            ])
            ->where('T1.park_id', $parkId)
            ->where('T1.contract_flag', 1)
            ->whereBetween(DB::raw('DATE(T1.contract_payment_day)'), [$startDate, $endDate])
            ->whereNotNull('T1.update_flag')
            ->whereNotNull('T1.psection_id')
            ->whereNotNull('T1.enable_months')
            ->groupBy([
                DB::raw('IFNULL(T1.contract_reduction, 0)'),
                'T1.update_flag',
                'T1.psection_id',
                'T3.usertype_subject1',
                'T1.enable_months'
            ])
            ->get();
        
        return $results->toArray();
    }

    /**
     * ② 一時金データ取得（車種毎）
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:148-159
     * 
     * @param int $parkId 駐輪場ID
     * @param string $startDate 集計開始日（YYYY-MM-DD）
     * @param string $endDate 集計終了日（YYYY-MM-DD）
     * @return array
     */
    private function calculateLumpsumEarnings(int $parkId, string $startDate, string $endDate): array
    {
        $results = DB::table('lumpsum_transaction')
            ->select([
                'type_class as psection_id',
                DB::raw('COUNT(*) as lumpsum_count'),
                DB::raw('COALESCE(SUM(deposit_amount), 0) as lumpsum')
            ])
            ->where('park_id', $parkId)
            ->whereBetween(DB::raw('DATE(pay_date)'), [$startDate, $endDate])
            ->groupBy('type_class')
            ->get();
        
        return $results->toArray();
    }

    /**
     * ③ 解約返戻金データ取得（車種区分毎）
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:160-171
     * 
     * @param int $parkId 駐輪場ID
     * @param string $startDate 集計開始日（YYYY-MM-DD）
     * @param string $endDate 集計終了日（YYYY-MM-DD）
     * @return array
     */
    private function calculateRefundEarnings(int $parkId, string $startDate, string $endDate): array
    {
        $results = DB::table('regular_contract')
            ->select([
                'psection_id',
                DB::raw('COALESCE(SUM(refunds), 0) as refunds')
            ])
            ->where('park_id', $parkId)
            ->where('contract_cancel_flag', 1)
            ->whereBetween(DB::raw('DATE(repayment_at)'), [$startDate, $endDate])
            ->groupBy('psection_id')
            ->get();
        
        return $results->toArray();
    }

    /**
     * ④ 再発行データ取得（車種区分毎）
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:172-183
     * 
     * @param int $parkId 駐輪場ID
     * @param string $startDate 集計開始日（YYYY-MM-DD）
     * @param string $endDate 集計終了日（YYYY-MM-DD）
     * @return array
     */
    private function calculateReissueCount(int $parkId, string $startDate, string $endDate): array
    {
        $results = DB::table('seal')
            ->select([
                'psection_id',
                DB::raw('COUNT(contract_id) as reissue_count')
            ])
            ->where('park_id', $parkId)
            ->where('contract_seal_issue', '>=', 2)
            ->whereBetween(DB::raw('DATE(seal_day)'), [$startDate, $endDate])
            ->groupBy('psection_id')
            ->get();
        
        return $results->toArray();
    }

    /**
     * 定期契約データを組合せ毎に統合
     * 
     * SQLは contract_reduction × update_flag で分組しているため、
     * 同じ psection × usertype × months の組合せで複数行が返る場合がある。
     * ここで park_id + psection_id + usertype_subject1 + enable_months をキーに統合し、
     * 新規/更新 × 減免/通常 の各件数・金額を1つのオブジェクトに集約する。
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:130-147
     * 
     * @param array $regularData calculateRegularEarnings()の結果
     * @return array キー：psection_id|usertype|months、値：統合されたデータオブジェクト
     */
    private function mergeRegularDataByGroup(array $regularData): array
    {
        $merged = [];
        
        foreach ($regularData as $row) {
            // 統合キー：psection_id|usertype_subject1|enable_months
            $key = $row->psection_id . '|' . $row->usertype_subject1 . '|' . $row->enable_months;
            
            // 初回作成
            if (!isset($merged[$key])) {
                $merged[$key] = (object)[
                    'psection_id' => $row->psection_id,
                    'usertype_subject1' => $row->usertype_subject1,
                    'enable_months' => $row->enable_months,
                    // 新規・通常
                    'regular_new_count' => 0,
                    'regular_new_amount' => 0,
                    // 新規・減免
                    'regular_new_reduction_count' => 0,
                    'regular_new_reduction_amount' => 0,
                    // 更新・通常
                    'regular_update_count' => 0,
                    'regular_update_amount' => 0,
                    // 更新・減免
                    'regular_update_reduction_count' => 0,
                    'regular_update_reduction_amount' => 0
                ];
            }
            
            // 区分判定
            $isNew = in_array($row->update_flag, [2, null]); // 新規
            $isReduction = ($row->contract_reduction == 1); // 減免
            
            $count = $row->contract_count ?? 0;
            $amount = $row->total_amount ?? 0;
            
            // 対応するフィールドに累加
            if ($isNew && !$isReduction) {
                // 新規・通常
                $merged[$key]->regular_new_count += $count;
                $merged[$key]->regular_new_amount += $amount;
            } elseif ($isNew && $isReduction) {
                // 新規・減免
                $merged[$key]->regular_new_reduction_count += $count;
                $merged[$key]->regular_new_reduction_amount += $amount;
            } elseif (!$isNew && !$isReduction) {
                // 更新・通常
                $merged[$key]->regular_update_count += $count;
                $merged[$key]->regular_update_amount += $amount;
            } elseif (!$isNew && $isReduction) {
                // 更新・減免
                $merged[$key]->regular_update_reduction_count += $count;
                $merged[$key]->regular_update_reduction_amount += $amount;
            }
        }
        
        return $merged;
    }

    /**
     * 一時金・解約・再発行データを車種区分毎に統合
     * 
     * @param array $lumpsumData
     * @param array $refundData
     * @param array $reissueData
     * @return array
     */
    private function mergeOtherEarningsData(array $lumpsumData, array $refundData, array $reissueData): array
    {
        $merged = [];
        
        // 一時金
        foreach ($lumpsumData as $row) {
            $psectionId = $row->psection_id;
            if (!isset($merged[$psectionId])) {
                $merged[$psectionId] = (object)[
                    'psection_id' => $psectionId,
                    'usertype_subject1' => null,
                    'enable_months' => 0,
                    'lumpsum_count' => 0,
                    'lumpsum' => 0,
                    'refunds' => 0,
                    'reissue_count' => 0
                ];
            }
            $merged[$psectionId]->lumpsum_count = $row->lumpsum_count;
            $merged[$psectionId]->lumpsum = $row->lumpsum;
        }
        
        // 解約返戻金
        foreach ($refundData as $row) {
            $psectionId = $row->psection_id;
            if (!isset($merged[$psectionId])) {
                $merged[$psectionId] = (object)[
                    'psection_id' => $psectionId,
                    'usertype_subject1' => null,
                    'enable_months' => 0,
                    'lumpsum_count' => 0,
                    'lumpsum' => 0,
                    'refunds' => 0,
                    'reissue_count' => 0
                ];
            }
            $merged[$psectionId]->refunds = $row->refunds;
        }
        
        // 再発行
        foreach ($reissueData as $row) {
            $psectionId = $row->psection_id;
            if (!isset($merged[$psectionId])) {
                $merged[$psectionId] = (object)[
                    'psection_id' => $psectionId,
                    'usertype_subject1' => null,
                    'enable_months' => 0,
                    'lumpsum_count' => 0,
                    'lumpsum' => 0,
                    'refunds' => 0,
                    'reissue_count' => 0
                ];
            }
            $merged[$psectionId]->reissue_count = $row->reissue_count;
        }
        
        return $merged;
    }

    /**
     * 【処理4】既存の売上集計結果を削除
     * 
     * 仕様書のキー：駐輪場ID, 集計種別, 集計開始日, 集計終了日, 売上日付, 車種区分, 分類名１, 定期有効月数
     * 仕様 todo/SHJ-10/SHJ-10.txt:213-221
     * 仕様書どおり summary_start_date と summary_end_date は NULL で保存されているため、NULL で検索する
     * 
     * @param int $parkId 駐輪場ID
     * @param array $aggregationTarget 集計対象
     * @return void
     */
    private function deleteExistingFiscalSummary(int $parkId, array $aggregationTarget): void
    {
        // 仕様書どおり、同一キーの組み合わせで削除
        DB::table('earnings_summary')
            ->where('park_id', $parkId)
            ->where('summary_type', $aggregationTarget['summary_type'])
            ->whereNull('summary_start_date')  // 仕様書line 251: null
            ->whereNull('summary_end_date')    // 仕様書line 252: null
            ->where('earnings_date', $aggregationTarget['end_date'])
            // psection_id, usertype_subject1, enable_months は
            // レコードごとに異なるため、ここでは指定しない
            ->delete();
            
        Log::debug('既存の売上集計結果削除', [
            'park_id' => $parkId,
            'summary_type' => $aggregationTarget['summary_type'],
            'earnings_date' => $aggregationTarget['end_date']
        ]);
    }

    /**
     * 売上集計結果を登録（財政年度ベース）
     * 
     * 仕様 todo/SHJ-10/SHJ-10.txt:215-284
     * 
     * @param object $park 駐輪場情報
     * @param object $data 売上データ
     * @param array $aggregationTarget 集計対象
     * @param array $fiscalPeriod 財政期間情報
     * @param string $dataType データ種別（regular or other）
     * @return void
     */
    private function createFiscalEarningsSummary($park, $data, array $aggregationTarget, array $fiscalPeriod, string $dataType): void
    {
        $insertData = [
            'park_id' => $park->park_id,
            'summary_type' => $aggregationTarget['summary_type'], // 1=年次, 2=月次
            'summary_start_date' => null, // 仕様書line 251: null
            'summary_end_date' => null,   // 仕様書line 252: null
            'earnings_date' => $aggregationTarget['end_date'], // 集計終了日
            'psection_id' => $data->psection_id,
            'usertype_subject1' => $data->usertype_subject1 ?? null, // 実際の分類名
            'enable_months' => $data->enable_months ?? 0, // 実際の定期有効月数
            'summary_note' => "SHJ-10：{$fiscalPeriod['target_label']}／{$aggregationTarget['start_date']}～{$aggregationTarget['end_date']}", // 仕様line 272
            'created_at' => now(),
            'updated_at' => now(),
            'operator_id' => self::BATCH_OPERATOR_ID // 9999999 (仕様line 275)
        ];
        
        if ($dataType === 'regular') {
            // 定期契約データの場合：mergeRegularDataByGroup()で既に統合済み
            // 新規/更新 × 減免/通常 の各件数・金額がすべて含まれている (仕様line 130-147)
            $insertData = array_merge($insertData, [
                'regular_new_count' => $data->regular_new_count ?? 0,
                'regular_new_amount' => $data->regular_new_amount ?? 0,
                'regular_new_reduction_count' => $data->regular_new_reduction_count ?? 0,
                'regular_new_reduction_amount' => $data->regular_new_reduction_amount ?? 0,
                'regular_update_count' => $data->regular_update_count ?? 0,
                'regular_update_amount' => $data->regular_update_amount ?? 0,
                'regular_update_reduction_count' => $data->regular_update_reduction_count ?? 0,
                'regular_update_reduction_amount' => $data->regular_update_reduction_amount ?? 0,
                'lumpsum_count' => 0, // 仕様line 140
                'lumpsum' => 0, // 仕様line 141
                'refunds' => 0, // 仕様line 142
                'other_income' => 0, // 仕様line 143
                'other_spending' => 0, // 仕様line 144
                'reissue_count' => 0, // 仕様line 145
                'reissue_amount' => 0 // 仕様line 146
            ]);
        } else {
            // 一時金・解約・再発行データの場合：定期フィールドは0固定 (仕様line 186-202)
            $insertData = array_merge($insertData, [
                'regular_new_count' => 0, // 仕様line 186
                'regular_new_amount' => 0, // 仕様line 187
                'regular_new_reduction_count' => 0, // 仕様line 188
                'regular_new_reduction_amount' => 0, // 仕様line 189
                'regular_update_count' => 0, // 仕様line 190
                'regular_update_amount' => 0, // 仕様line 191
                'regular_update_reduction_count' => 0, // 仕様line 192
                'regular_update_reduction_amount' => 0, // 仕様line 193
                'lumpsum_count' => $data->lumpsum_count ?? 0, // 仕様line 194
                'lumpsum' => $data->lumpsum ?? 0, // 仕様line 195
                'refunds' => $data->refunds ?? 0, // 仕様line 196
                'other_income' => 0, // 仕様line 197
                'other_spending' => 0, // 仕様line 198
                'reissue_count' => $data->reissue_count ?? 0, // 仕様line 199
                'reissue_amount' => 0 // 仕様line 200: 0固定
            ]);
        }
        
        DB::table('earnings_summary')->insert($insertData);
        
        Log::debug('売上集計結果登録', [
            'park_id' => $park->park_id,
            'psection_id' => $data->psection_id,
            'data_type' => $dataType,
            'summary_type' => $aggregationTarget['summary_type']
        ]);
    }

    /**
     * 【処理5】オペレータキュー作成（駐輪場単位・情報不備がある場合のみ）
     *
     * 仕様 todo/SHJ-10/SHJ-10.txt:289-317
     * - que_class: 14（集計対象エラー）
     * - que_comment: 空（仕様書line 297）
     * - que_status: 1（キュー発生）
     * - que_status_comment: 空（仕様書line 299）
     * - work_instructions: 情報不備メッセージ（仕様書line 300）
     * - park_id: 駐輪場ID（仕様 "処理1.駐輪場ID"、パラメータエラー時はnull）
     * - operator_id: 9999999（バッチ処理固定値）
     *
     * @param string $message 情報不備メッセージ
     * @param int|null $parkId 駐輪場ID（パラメータエラー時はnull）
     * @return void
     */
    private function createOperatorQueue(string $message, ?int $parkId = null): void
    {
        try {
            DB::table('operator_que')->insert([
                'que_class' => 14, // 集計対象エラー
                'user_id' => null,
                'contract_id' => null,
                'park_id' => $parkId, // 仕様：処理1.駐輪場ID
                'que_comment' => '', // 仕様: 空
                'que_status' => 1, // キュー発生
                'que_status_comment' => '', // 仕様: 空
                'work_instructions' => $message, // 仕様: 情報不備
                'operator_id' => self::BATCH_OPERATOR_ID, // 9999999
                'created_at' => now(),
                'updated_at' => now()
            ]);

            Log::info('オペレータキュー作成完了', [
                'park_id' => $parkId,
                'que_class' => 14,
                'que_status' => 1,
                'operator_id' => self::BATCH_OPERATOR_ID,
                'work_instructions' => $message
            ]);

        } catch (\Exception $e) {
            Log::error('オペレータキュー作成エラー', [
                'park_id' => $parkId,
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * SHJ-8 バッチ処理ログ作成
     *
     * 共通処理「SHJ-8 バッチ処理ログ作成」を呼び出す
     *
     * @param string $jobName ジョブ名
     * @param string $status ステータス (success/error)
     * @param string $statusComment 業務固有のステータスコメント
     * @return void
     */
    private function callShjEight(string $jobName, string $status, string $statusComment): void
    {
        try {
            $device = Device::orderBy('device_id')->first();
            $deviceId = $device ? $device->device_id : 1;

            $today = now()->format('Y/m/d');

            $this->shjEightService->execute(
                $deviceId,
                'SHJ-10',
                $jobName,
                $status,
                $statusComment,
                $today,
                $today
            );

            Log::info('SHJ-8 バッチ処理ログ作成完了', [
                'job_name' => $jobName,
                'status' => $status
            ]);

        } catch (\Exception $e) {
            Log::error('SHJ-8 バッチ処理ログ作成エラー', [
                'error' => $e->getMessage(),
                'job_name' => $jobName,
                'status_comment' => $statusComment
            ]);
            throw $e;
        }
    }

    /**
     * 集計種別のラベル取得
     * 
     * @param string $type 集計種別
     * @return string ラベル
     */
    private function getTypeLabel(string $type): string
    {
        switch ($type) {
            case 'yearly':
                return '（年次）';
            case 'monthly':
                return '（月次）';
            default:
                return '';
        }
    }
}
