<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\Batch\BatchLog;
use App\Models\RegularContract;
use App\Models\Park;
use App\Models\Psection;
use App\Models\Ptype;
use Carbon\Carbon;

/**
 * SHJ-11 現在契約台数集計サービス
 * 
 * 集計単位每个の契約台数を算出し、ゾーンマスタとの管理処理を実行
 */
class ShjElevenService
{
    /**
     * 【処理1】集計単位每个の契約台数を算出する
     * 
     * 集計単位: 駐輪場ID + 車種区分ID + 駐輪分類ID + ゾーンID
     * 
     * SQL仕様に基づく複雑なJOIN処理:
     * - regular_contract (T1) 
     * - park (T2)
     * - psection (T4)
     * - price_a (T5) 
     * - ptype (T3)
     *
     * @return array 契約台数集計結果
     */
    public function calculateContractCounts(): array
    {
        try {
            $query = DB::table('regular_contract as T1')
                ->select([
                    'T1.park_id',               // 駐輪場ID
                    'T2.park_name',             // 駐輪場名
                    'T5.psection_id',           // 車種区分ID
                    'T4.psection_subject',      // 車種区分名
                    'T5.ptype_id',              // 駐輪分類ID
                    'T3.ptype_subject',         // 駐輪分類名
                    'T1.zone_id',               // ゾーンID
                    DB::raw('count(T1.contract_id) as cnt')  // 契約台数
                ])
                // park テーブルとの JOIN
                ->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
                // psection テーブルとの JOIN
                ->join('psection as T4', 'T1.psection_id', '=', 'T4.psection_id')
                // price_a テーブルとの複合条件 JOIN
                ->join('price_a as T5', function($join) {
                    $join->on('T1.park_id', '=', 'T5.park_id')
                         ->on('T1.price_parkplaceid', '=', 'T5.price_parkplaceid')
                         ->on('T1.psection_id', '=', 'T5.psection_id');
                })
                // ptype テーブルとの JOIN
                ->join('ptype as T3', 'T5.ptype_id', '=', 'T3.ptype_id')
                ->where([
                    ['T1.contract_flag', '=', 1],           // 有効契約
                    ['T2.park_close_flag', '=', 0],         // 駐輪場未閉鎖
                ])
                // 契約有効期間内の条件
                ->whereRaw("date_format(now(), '%y %m %d') BETWEEN T1.contract_periods AND T1.contract_periode")
                ->groupBy([
                    'T1.park_id',
                    'T2.park_name',
                    'T5.psection_id',
                    'T4.psection_subject',
                    'T5.ptype_id',
                    'T3.ptype_subject',
                    'T1.zone_id'
                ])
                ->get();

            Log::info('SHJ-11 契約台数算出完了', [
                'count' => $query->count(),
                'sql_conditions' => [
                    'contract_flag' => 1,
                    'park_close_flag' => 0,
                    'contract_period_check' => 'BETWEEN contract_periods AND contract_periode'
                ]
            ]);

            return $query->toArray();

        } catch (\Exception $e) {
            Log::error('SHJ-11 契約台数算出エラー', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            throw $e;
        }
    }

    /**
     * 【処理2・3】ゾーンマスタ管理処理
     * 
     * 処理フロー:
     * 1. ゾーンマスタを取得する
     * 2. 取得判定 → 存在しない場合は新規登録
     * 3. 契約台数チェック（限界台数超過判定）
     * 4. 契約台数を反映する（ゾーンマスタ更新）
     *
     * @param array $contractCounts 契約台数集計結果
     * @return array 処理結果
     */
    public function processZoneManagement(array $contractCounts): array
    {
        $createdZones = 0;
        $updatedZones = 0;
        $overCapacityCount = 0;
        $errors = [];
        $processParameters = [];

        try {
            DB::beginTransaction();

            foreach ($contractCounts as $contractData) {
                try {
                    // 【処理2】ゾーンマスタを取得する
                    $zoneData = $this->getZoneData($contractData);

                    if (!$zoneData) {
                        // 【判断2】ゾーンマスタが存在しない場合 → 新規登録
                        $createResult = $this->createZoneData($contractData);
                        if ($createResult['success']) {
                            $createdZones++;
                        }
                        $zoneData = $createResult['zone_data'];
                    }

                    // 【判断3】契約台数チェック（限界台数超過判定）
                    $isOverCapacity = $this->checkCapacityLimit($contractData, $zoneData);
                    if ($isOverCapacity) {
                        $overCapacityCount++;
                        Log::warning('SHJ-11 限界台数超過検出', [
                            'park_id' => $contractData->park_id,
                            'zone_id' => $contractData->zone_id,
                            'current_count' => $contractData->cnt,
                            'limit_capacity' => $zoneData->zone_tolerance ?? 0
                        ]);
                    }

                    // 【処理3】契約台数を反映する（ゾーンマスタ更新）
                    $updateResult = $this->updateZoneContractCount($contractData);
                    if ($updateResult['success']) {
                        $updatedZones++;
                    }

                    // 処理パラメータ記録
                    $processParameters[] = [
                        'park_id' => $contractData->park_id,
                        'psection_id' => $contractData->psection_id,
                        'ptype_id' => $contractData->ptype_id,
                        'zone_id' => $contractData->zone_id,
                        'contract_count' => $contractData->cnt,
                        'is_over_capacity' => $isOverCapacity,
                        'zone_created' => !$zoneData && $createResult['success'] ?? false,
                        'zone_updated' => $updateResult['success'] ?? false
                    ];

                } catch (\Exception $e) {
                    $errors[] = [
                        'park_id' => $contractData->park_id ?? null,
                        'zone_id' => $contractData->zone_id ?? null,
                        'error' => $e->getMessage()
                    ];
                    
                    Log::warning('SHJ-11 個別処理エラー', [
                        'park_id' => $contractData->park_id ?? null,
                        'zone_id' => $contractData->zone_id ?? null,
                        'error' => $e->getMessage()
                    ]);
                }
            }

            DB::commit();

            return [
                'success' => true,
                'created_zones' => $createdZones,
                'updated_zones' => $updatedZones,
                'over_capacity_count' => $overCapacityCount,
                'parameters' => $processParameters,
                'errors' => $errors,
                'message' => '現在契約台数集計処理完了'
            ];

        } catch (\Exception $e) {
            DB::rollBack();
            
            Log::error('SHJ-11 ゾーンマスタ管理処理全体エラー', [
                'error' => $e->getMessage(),
                'processed_count' => count($processParameters)
            ]);

            return [
                'success' => false,
                'created_zones' => $createdZones,
                'updated_zones' => $updatedZones,
                'over_capacity_count' => $overCapacityCount,
                'parameters' => $processParameters,
                'errors' => $errors,
                'message' => 'ゾーンマスタ管理処理エラー: ' . $e->getMessage(),
                'details' => $e->getTraceAsString()
            ];
        }
    }

    /**
     * ゾーンマスタデータ取得
     * 
     * 集計単位に対応するゾーンマスタを取得
     *
     * @param object $contractData 契約台数集計データ
     * @return object|null ゾーンマスタデータ
     */
    private function getZoneData($contractData)
    {
        try {
            return DB::table('zone')
                ->select([
                    'zone_id',
                    'park_id',
                    'ptype_id',
                    'psection_id',
                    'zone_name',
                    'zone_number',          // 現在契約台数
                    'zone_standard',        // 標準収容台数
                    'zone_tolerance'        // 限界収容台数
                ])
                ->where([
                    ['park_id', '=', $contractData->park_id],
                    ['psection_id', '=', $contractData->psection_id],
                    ['ptype_id', '=', $contractData->ptype_id],
                    ['zone_id', '=', $contractData->zone_id]
                ])
                ->first();

        } catch (\Exception $e) {
            Log::error('SHJ-11 ゾーンマスタ取得エラー', [
                'park_id' => $contractData->park_id,
                'zone_id' => $contractData->zone_id,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * ゾーンマスタ新規作成
     * 
     * 仕様書に基づくINSERT処理
     *
     * @param object $contractData 契約台数集計データ
     * @return array 作成結果
     */
    private function createZoneData($contractData): array
    {
        try {
            // 新規ゾーンマスタのデフォルト値設定
            $newZoneData = [
                'zone_id' => $contractData->zone_id,
                'park_id' => $contractData->park_id,
                'ptype_id' => $contractData->ptype_id,
                'psection_id' => $contractData->psection_id,
                'zone_name' => null,                           // デフォルトnull
                'zone_number' => $contractData->cnt,           // 算出した契約台数
                'zone_standard' => null,                       // デフォルトnull
                'zone_tolerance' => null,                      // デフォルトnull
                'zone_sort' => null,                          // デフォルトnull
                'delete_flag' => 0,                           // 削除フラグOFF
                'created_at' => now(),
                'updated_at' => now(),
                'ope_id' => 9999999                           // 仕様書指定値
            ];

            DB::table('zone')->insert($newZoneData);

            Log::info('SHJ-11 ゾーンマスタ新規作成完了', [
                'zone_id' => $contractData->zone_id,
                'park_id' => $contractData->park_id,
                'contract_count' => $contractData->cnt
            ]);

            return [
                'success' => true,
                'zone_data' => (object) $newZoneData,
                'message' => 'ゾーンマスタを新規作成しました'
            ];

        } catch (\Exception $e) {
            Log::error('SHJ-11 ゾーンマスタ新規作成エラー', [
                'zone_id' => $contractData->zone_id,
                'park_id' => $contractData->park_id,
                'error' => $e->getMessage()
            ]);

            return [
                'success' => false,
                'zone_data' => null,
                'message' => 'ゾーンマスタ新規作成エラー: ' . $e->getMessage()
            ];
        }
    }

    /**
     * 契約台数限界チェック
     * 
     * 【判断3】契約台数 > 限界収容台数の判定
     *
     * @param object $contractData 契約台数集計データ
     * @param object|null $zoneData ゾーンマスタデータ
     * @return bool 限界台数超過フラグ
     */
    private function checkCapacityLimit($contractData, $zoneData = null): bool
    {
        if (!$zoneData || is_null($zoneData->zone_tolerance)) {
            // ゾーンデータが存在しないか、限界収容台数が未設定の場合は超過判定しない
            return false;
        }

        // 契約台数 > 限界収容台数の判定
        return $contractData->cnt > $zoneData->zone_tolerance;
    }

    /**
     * ゾーンマスタ契約台数更新
     * 
     * 【処理3】現在契約台数をゾーンマスタに反映
     *
     * @param object $contractData 契約台数集計データ
     * @return array 更新結果
     */
    private function updateZoneContractCount($contractData): array
    {
        try {
            $updateData = [
                'zone_number' => $contractData->cnt,           // 現在契約台数を更新
                'updated_at' => now(),                         // 更新日時
                'ope_id' => 'SHJ-11'                          // 更新オペレータID
            ];

            $updated = DB::table('zone')
                ->where([
                    ['park_id', '=', $contractData->park_id],
                    ['psection_id', '=', $contractData->psection_id],
                    ['ptype_id', '=', $contractData->ptype_id],
                    ['zone_id', '=', $contractData->zone_id]
                ])
                ->update($updateData);

            if ($updated > 0) {
                Log::info('SHJ-11 ゾーンマスタ更新完了', [
                    'zone_id' => $contractData->zone_id,
                    'park_id' => $contractData->park_id,
                    'new_contract_count' => $contractData->cnt
                ]);

                return [
                    'success' => true,
                    'message' => 'ゾーンマスタ契約台数を更新しました'
                ];
            } else {
                return [
                    'success' => false,
                    'message' => 'ゾーンマスタ更新対象が見つかりません'
                ];
            }

        } catch (\Exception $e) {
            Log::error('SHJ-11 ゾーンマスタ更新エラー', [
                'zone_id' => $contractData->zone_id,
                'park_id' => $contractData->park_id,
                'error' => $e->getMessage()
            ]);

            return [
                'success' => false,
                'message' => 'ゾーンマスタ更新エラー: ' . $e->getMessage()
            ];
        }
    }

    /**
     * 【処理4】バッチ処理ログを作成する
     * 
     * 統一BatchLogシステムを使用してSHJ-11の実行ログを記録
     *
     * @param string $status ステータス
     * @param array $parameters パラメータ
     * @param string $message メッセージ
     * @param int $executionCount 実行回数
     * @param int $successCount 成功回数
     * @param int $errorCount エラー回数
     * @return void
     */
    public function createBatchLog(
        string $status,
        array $parameters,
        string $message,
        int $executionCount = 0,
        int $successCount = 0,
        int $errorCount = 0
    ): void {
        try {
            BatchLog::createBatchLog(
                'SHJ-11',
                $status,
                $parameters,
                $message,
                [
                    'execution_count' => $executionCount,
                    'success_count' => $successCount,
                    'error_count' => $errorCount,
                    'process_type' => '現在契約台数集計',
                    'executed_at' => now()->toISOString()
                ]
            );

        } catch (\Exception $e) {
            Log::error('SHJ-11 バッチログ作成エラー', [
                'error' => $e->getMessage(),
                'status' => $status,
                'message' => $message
            ]);
        }
    }
}
