<?php

namespace App\Services;

use App\Models\Park;
use App\Models\RegularContract;
use App\Models\Device;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

/**
 * SHJ-4C 室割当処理サービス
 * 
 * ゾーン情報取得及び割当処理を実行するビジネスロジック
 * バッチ処理「SHJ-4C室割当」の核となる処理を担当
 */
class ShjFourCService
{
    /**
     * Park モデル
     *
     * @var Park
     */
    protected $parkModel;

    /**
     * RegularContract モデル
     *
     * @var RegularContract
     */
    protected $contractModel;

    /**
     * ShjEightService
     *
     * @var ShjEightService
     */
    protected $shjEightService;

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

    /**
     * SHJ-4C 室割当処理メイン実行
     * 
     * 処理フロー:
     * 【処理1】ゾーン情報取得
     * 【判断1】割当判定
     * 【処理2】バッチログ作成  
     * 【処理3】処理結果返却
     *
     * @param int $parkId 駐輪場ID
     * @param int $ptypeId 駐輪分類ID
     * @param int $psectionId 車種区分ID
     * @return array 処理結果
     */
    public function executeRoomAllocation(int $parkId, int $ptypeId, int $psectionId): array
    {
        $statusComment = '';
        $status = 'success';

        try {
            Log::info('SHJ-4C 室割当処理開始', [
                'park_id' => $parkId,
                'ptype_id' => $ptypeId,
                'psection_id' => $psectionId
            ]);

            // 【処理1】ゾーン情報取得
            $zoneInfo = $this->getZoneInformation($parkId, $ptypeId, $psectionId);

            if (empty($zoneInfo)) {
                $message = '対象のゾーン情報が見つかりません';
                $status = 'error';
                $statusComment = sprintf('エラー: %s (park_id:%d, ptype_id:%d, psection_id:%d)',
                    $message, $parkId, $ptypeId, $psectionId);

                // バッチログ作成
                $this->createBatchLog($status, $statusComment);

                return [
                    'success' => false,
                    'message' => $message
                ];
            }

            // 【判断1】割当判定処理
            $allocationResult = $this->performAllocationJudgment($zoneInfo, $parkId, $ptypeId, $psectionId);

            if (!$allocationResult['can_allocate']) {
                // 割当NGの場合、対象事室番号を設定
                $this->setTargetRoomNumber($allocationResult['target_room_number']);

                $status = 'warning';
                $statusComment = sprintf('割当NG: %s (park_id:%d, ptype_id:%d, psection_id:%d)',
                    $allocationResult['reason'], $parkId, $ptypeId, $psectionId);

                // バッチログ作成
                $this->createBatchLog($status, $statusComment);

                return [
                    'success' => true,
                    'message' => '割当判定完了（割当NG）',
                    'allocation_result' => $allocationResult
                ];
            }

            // 【処理2】割当実行（割当OKの場合）
            $executionResult = $this->executeAllocation($zoneInfo, $allocationResult);

            $statusComment = sprintf('室割当処理完了 (park_id:%d, ptype_id:%d, psection_id:%d, zone_id:%s)',
                $parkId, $ptypeId, $psectionId, $allocationResult['target_room_number'] ?? 'N/A');

            // バッチログ作成
            $this->createBatchLog($status, $statusComment);

            Log::info('SHJ-4C 室割当処理完了', [
                'execution_result' => $executionResult
            ]);

            // 【処理3】処理結果返却
            return [
                'success' => true,
                'message' => 'SHJ-4C 室割当処理が正常に完了しました',
                'zone_info' => $zoneInfo,
                'allocation_result' => $allocationResult,
                'execution_result' => $executionResult
            ];

        } catch (\Exception $e) {
            $errorMessage = 'SHJ-4C 室割当処理でエラーが発生: ' . $e->getMessage();
            $status = 'error';
            $statusComment = sprintf('例外エラー: %s (park_id:%d, ptype_id:%d, psection_id:%d)',
                $e->getMessage(), $parkId, $ptypeId, $psectionId);

            // バッチログ作成
            $this->createBatchLog($status, $statusComment);

            Log::error('SHJ-4C 室割当処理エラー', [
                'exception' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

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

    /**
     * SHJ-8バッチ処理ログ作成
     *
     * @param string $status ステータス
     * @param string $statusComment ステータスコメント
     * @return void
     */
    private function createBatchLog(string $status, string $statusComment): void
    {
        try {
            $device = Device::orderBy('device_id')->first();
            $deviceId = $device ? $device->device_id : 1;

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

            Log::info('SHJ-8バッチ処理ログ作成', [
                'device_id' => $deviceId,
                'process_name' => 'SHJ-4C',
                'job_name' => 'SHJ-4C室割当',
                'status' => $status,
                'status_comment' => $statusComment
            ]);

            // SHJ-8サービスを呼び出し
            $this->shjEightService->execute(
                $deviceId,
                'SHJ-4C',
                'SHJ-4C室割当',
                $status,
                $statusComment,
                $today,
                $today
            );

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

    /**
     * 【処理1】ゾーン情報取得
     * 
     * 駐輪場ID、駐輪分類ID、車種区分IDに紐づくゾーン情報を取得する
     * SQLクエリは設計書の仕様に基づく
     *
     * @param int $parkId 駐輪場ID
     * @param int $ptypeId 駐輪分類ID
     * @param int $psectionId 車種区分ID
     * @return array ゾーン情報
     */
    private function getZoneInformation(int $parkId, int $ptypeId, int $psectionId): array
    {
        try {
            // 設計書に記載されたSQLクエリに基づくゾーン情報取得
            $zoneInfo = DB::table('zone as T1')
                ->select([
                    'T1.zone_id',
                    'T1.zone_name', 
                    'T1.zone_contracted_count as zone_contracted_count',
                    'T1.zone_capacity as zone_capacity',
                    'T1.zone_permitted_count as zone_permitted_count',
                    'T1.zone_sort',
                    'T2.update_grace_period_start_date',
                    'T2.update_grace_period_end_date'
                ])
                ->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
                ->where('T1.park_id', $parkId)
                ->where('T1.ptype_id', $ptypeId)
                ->where('T1.psection_id', $psectionId)
                ->where('T1.delete_flag', 0)
                ->orderBy('T1.zone_sort')
                ->get()
                ->toArray();

            Log::info('ゾーン情報取得完了', [
                'park_id' => $parkId,
                'ptype_id' => $ptypeId,
                'psection_id' => $psectionId,
                'zone_count' => count($zoneInfo)
            ]);

            return $zoneInfo;

        } catch (\Exception $e) {
            Log::error('ゾーン情報取得エラー', [
                'park_id' => $parkId,
                'ptype_id' => $ptypeId,
                'psection_id' => $psectionId,
                'error' => $e->getMessage()
            ]);

            throw $e;
        }
    }

    /**
     * 【判断1】割当判定処理
     * 
     * ゾーン内の定期契約マスタから該当レコード数を取得し、
     * 定期契約マスタから最適な車室番号を選定する割当判定を実行
     *
     * @param array $zoneInfo ゾーン情報
     * @param int $parkId 駐輪場ID
     * @param int $ptypeId 駐輪分類ID
     * @param int $psectionId 車種区分ID
     * @return array 割当判定結果
     */
    private function performAllocationJudgment(array $zoneInfo, int $parkId, int $ptypeId, int $psectionId): array
    {
        try {
            foreach ($zoneInfo as $zone) {
                // 各ゾーンに対する定期契約情報取得
                $contractInfo = $this->getRegularContractInfo($parkId, $zone->zone_id, $ptypeId, $psectionId);

                // 割当可能性判定
                if ($this->canAllocateToZone($zone, $contractInfo)) {
                    return [
                        'can_allocate' => true,
                        'zone_id' => $zone->zone_id,
                        'zone_name' => $zone->zone_name,
                        'contract_info' => $contractInfo,
                        'reason' => '割当OK: ゾーンID ' . $zone->zone_id
                    ];
                }
            }

            // 全ゾーンで割当NGの場合
            $targetRoomNumber = $this->generateTargetRoomNumber($parkId, $ptypeId, $psectionId);

            return [
                'can_allocate' => false,
                'target_room_number' => $targetRoomNumber,
                'reason' => '全ゾーンで割当できませんでした'
            ];

        } catch (\Exception $e) {
            Log::error('割当判定処理エラー', [
                'error' => $e->getMessage()
            ]);

            throw $e;
        }
    }

    /**
     * 定期契約情報取得
     * 
     * 設計書のSQLクエリに基づく定期契約マスタ検索
     *
     * @param int $parkId 駐輪場ID
     * @param int $zoneId ゾーンID
     * @param int $ptypeId 駐輪分類ID
     * @param int $psectionId 車種区分ID
     * @return array 定期契約情報
     */
    private function getRegularContractInfo(int $parkId, int $zoneId, int $ptypeId, int $psectionId): array
    {
        $currentDate = Carbon::now()->format('Y-m-d');

        $contractInfo = DB::table('regular_contract as T1')
            ->select(['T1.contract_id'])
            ->where('T1.park_id', $parkId)
            ->where('T1.zone_id', $zoneId)
            ->where('T1.ptype_id', $ptypeId)
            ->where('T1.psection_id', $psectionId)
            ->where('T1.place_no', '!=', '')
            ->where(function ($query) use ($currentDate) {
                $query->where(function ($subQuery) use ($currentDate) {
                    // パターンA: 処理1.更新期間開始日 <= 処理1.更新期間終了日 の場合
                    $subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')")
                             ->whereRaw("date_format(now(), '%y-%m-%d') <= date_format(now(), '%y-%m-%d')");
                })
                ->orWhere(function ($subQuery) use ($currentDate) {
                    // パターンB: その他の場合
                    $subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')");
                });
            })
            ->where('T1.contract_flag', 1)
            ->get()
            ->toArray();

        return $contractInfo;
    }

    /**
     * ゾーン割当可能性判定
     *
     * @param object $zone ゾーン情報
     * @param array $contractInfo 契約情報
     * @return bool 割当可能かどうか
     */
    private function canAllocateToZone($zone, array $contractInfo): bool
    {
        $contractedCount = count($contractInfo);
        $capacity = $zone->zone_capacity;

        // 空きがある場合は割当可能
        return $contractedCount < $capacity;
    }

    /**
     * 対象事室番号生成
     *
     * @param int $parkId 駐輪場ID
     * @param int $ptypeId 駐輪分類ID
     * @param int $psectionId 車種区分ID
     * @return string 対象事室番号
     */
    private function generateTargetRoomNumber(int $parkId, int $ptypeId, int $psectionId): string
    {
        return sprintf('%d_%d_%d', $parkId, $ptypeId, $psectionId);
    }

    /**
     * 対象事室番号設定
     *
     * @param string $targetRoomNumber 対象事室番号
     * @return void
     */
    private function setTargetRoomNumber(string $targetRoomNumber): void
    {
        Log::info('対象事室番号設定', [
            'target_room_number' => $targetRoomNumber
        ]);

        // 実際の事室番号設定ロジックをここに実装
        // 具体的な仕様が必要な場合は後で追加実装
    }

    /**
     * 割当実行
     *
     * @param array $zoneInfo ゾーン情報
     * @param array $allocationResult 割当判定結果
     * @return array 実行結果
     */
    private function executeAllocation(array $zoneInfo, array $allocationResult): array
    {
        // 割当実行の具体的なロジックを実装
        // 設計書に詳細仕様があれば追加実装

        return [
            'executed' => true,
            'zone_id' => $allocationResult['zone_id'],
            'message' => '割当実行完了'
        ];
    }


}