<?php

namespace App\Services;

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

/**
 * SHJ-13 契約台数追加処理サービス
 *
 * 新規契約時の契約台数を park_number・zone テーブルに反映する処理
 * SHJ-4B の副作用処理として実行される
 */
class ShjThirteenService
{
    /**
     * ShjEightService
     *
     * @var ShjEightService
     */
    protected $shjEightService;

    /**
     * コンストラクタ
     *
     * @param ShjEightService $shjEightService
     */
    public function __construct(ShjEightService $shjEightService)
    {
        $this->shjEightService = $shjEightService;
    }
    /**
     * SHJ-13 契約台数追加処理実行
     * 
     * 【処理1】契約台数を反映する - park_number・zone テーブルの契約台数を+1更新
     * 【処理2】バッチ処理ログを作成する - SHJ-8共通仕様でログ登録
     *
     * @param array $contractData 契約データ
     * @return array 処理結果
     */
    public function execute(array $contractData): array
    {
        $startTime = now();
        
        Log::info('SHJ-13 契約台数追加処理開始', [
            'contract_id' => $contractData['contract_id'] ?? null,
            'park_id' => $contractData['park_id'] ?? null,
            'psection_id' => $contractData['psection_id'] ?? null,
            'ptype_id' => $contractData['ptype_id'] ?? null,
            'zone_id' => $contractData['zone_id'] ?? null,
        ]);

        try {
            // パラメータ検証
            $validationResult = $this->validateParameters($contractData);
            if (!$validationResult['valid']) {
                return $this->createErrorResult(
                    1001, 
                    'パラメータエラー: ' . $validationResult['message']
                );
            }

            // 【処理1・2】契約台数反映とバッチログ作成を一体で実行
            $processResult = $this->executeProcessWithLogging($contractData);
            if (!$processResult['success']) {
                return $this->createErrorResult(
                    $processResult['error_code'],
                    $processResult['error_message'],
                    $processResult['stack_trace'] ?? ''
                );
            }

            $statusComment = $processResult['status_comment'];

            $endTime = now();
            Log::info('SHJ-13 契約台数追加処理完了', [
                'contract_id' => $contractData['contract_id'],
                'execution_time' => $startTime->diffInSeconds($endTime),
                'updated_count' => $processResult['updated_count'],
                'status_comment' => $statusComment,
            ]);

            return ['result' => 0];

        } catch (\Throwable $e) {
            Log::error('SHJ-13 契約台数追加処理例外エラー', [
                'contract_data' => $contractData,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            return $this->createErrorResult(
                $e->getCode() ?: 1999,
                $e->getMessage(),
                $e->getTraceAsString()
            );
        }
    }

    /**
     * パラメータ検証
     *
     * @param array $contractData
     * @return array
     */
    private function validateParameters(array $contractData): array
    {
        $errors = [];

        if (empty($contractData['park_id'])) {
            $errors[] = '駐輪場IDが設定されていません';
        }

        if (empty($contractData['psection_id'])) {
            $errors[] = '車種区分IDが設定されていません';
        }

        if (empty($contractData['ptype_id'])) {
            $errors[] = '駐輪分類IDが設定されていません';
        }

        if (empty($contractData['zone_id'])) {
            $errors[] = 'ゾーンIDが設定されていません';
        }

        if (!empty($errors)) {
            return [
                'valid' => false,
                'message' => implode(', ', $errors),
                'details' => $errors,
            ];
        }

        return ['valid' => true];
    }

    /**
     * 契約台数反映とバッチログ作成の一体処理
     * 
     * park_number・zone テーブルの契約台数を+1更新し、バッチログを作成
     * 全てを1つのトランザクション内で実行
     *
     * @param array $contractData
     * @return array
     */
    private function executeProcessWithLogging(array $contractData): array
    {
        try {
            return DB::transaction(function() use ($contractData) {
                // 各テーブルの名称取得
                $names = $this->getTableNames($contractData);
                if (!$names['success']) {
                    throw new \Exception('名称取得エラー: ' . $names['message'], 1002);
                }

                // park_number テーブル更新
                $parkNumberUpdated = DB::table('park_number')
                    ->where('park_id', $contractData['park_id'])
                    ->where('psection_id', $contractData['psection_id'])
                    ->where('ptype_id', $contractData['ptype_id'])
                    ->increment('park_number', 1, [
                        'updated_at' => now(),
                    ]);

                if ($parkNumberUpdated === 0) {
                    throw new \Exception('park_numberテーブルの対象レコードが存在しません', 1011);
                }

                // zone テーブル更新
                $zoneUpdated = DB::table('zone')
                    ->where('zone_id', $contractData['zone_id'])
                    ->increment('zone_number', 1, [
                        'updated_at' => now(),
                    ]);

                if ($zoneUpdated === 0) {
                    throw new \Exception('zoneテーブルの対象レコードが存在しません', 1012);
                }

                // 更新後の契約台数取得
                $updatedZone = DB::table('zone')
                    ->where('zone_id', $contractData['zone_id'])
                    ->first(['zone_number']);

                // ステータスコメント構築
                $statusComment = $this->buildStatusComment($names['names'], $updatedZone->zone_number);

                // バッチ処理ログ作成（同一トランザクション内）
                // SHJ-8サービスを呼び出し
                $device = Device::orderBy('device_id')->first();
                $deviceId = $device ? $device->device_id : 1;
                $today = now()->format('Y/m/d');

                $this->shjEightService->execute(
                    $deviceId,
                    'SHJ-13',
                    'SHJ-13契約台数追加',
                    'success',
                    $statusComment,
                    $today,
                    $today
                );

                Log::info('SHJ-13 契約台数更新・ログ作成完了', [
                    'contract_id' => $contractData['contract_id'],
                    'park_number_updated' => $parkNumberUpdated,
                    'zone_updated' => $zoneUpdated,
                    'updated_count' => $updatedZone->zone_number,
                    'status_comment' => $statusComment,
                ]);

                return [
                    'success' => true,
                    'updated_count' => $updatedZone->zone_number,
                    'status_comment' => $statusComment,
                ];
            });

        } catch (\Throwable $e) {
            Log::error('SHJ-13 契約台数更新・ログ作成エラー', [
                'contract_data' => $contractData,
                'error_code' => $e->getCode(),
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            return [
                'success' => false,
                'error_code' => $e->getCode() ?: 1010,
                'error_message' => $e->getMessage(),
                'stack_trace' => $e->getTraceAsString(),
            ];
        }
    }

    /**
     * テーブル名称取得
     *
     * @param array $contractData
     * @return array
     */
    private function getTableNames(array $contractData): array
    {
        try {
            // 駐輪場名取得
            $park = DB::table('park')
                ->where('park_id', $contractData['park_id'])
                ->first(['park_name']);

            if (!$park) {
                return [
                    'success' => false,
                    'message' => "駐輪場が見つかりません: park_id={$contractData['park_id']}",
                ];
            }

            // 駐輪分類名取得
            $ptype = DB::table('ptype')
                ->where('ptype_id', $contractData['ptype_id'])
                ->first(['ptype_subject']);

            if (!$ptype) {
                return [
                    'success' => false,
                    'message' => "駐輪分類が見つかりません: ptype_id={$contractData['ptype_id']}",
                ];
            }

            // 車種区分名取得
            $psection = DB::table('psection')
                ->where('psection_id', $contractData['psection_id'])
                ->first(['psection_subject']);

            if (!$psection) {
                return [
                    'success' => false,
                    'message' => "車種区分が見つかりません: psection_id={$contractData['psection_id']}",
                ];
            }

            // ゾーン名取得
            $zone = DB::table('zone')
                ->where('zone_id', $contractData['zone_id'])
                ->first(['zone_name']);

            if (!$zone) {
                return [
                    'success' => false,
                    'message' => "ゾーンが見つかりません: zone_id={$contractData['zone_id']}",
                ];
            }

            return [
                'success' => true,
                'names' => [
                    'park_name' => $park->park_name,
                    'ptype_subject' => $ptype->ptype_subject,
                    'psection_subject' => $psection->psection_subject,
                    'zone_name' => $zone->zone_name,
                ],
            ];

        } catch (\Throwable $e) {
            return [
                'success' => false,
                'message' => 'テーブル名称取得エラー: ' . $e->getMessage(),
                'details' => $e->getTraceAsString(),
            ];
        }
    }

    /**
     * ステータスコメント構築
     * 
     * 形式：駐輪場名／駐輪分類名／車種区分名／ゾーン名／現在契約台数（更新後）：{数値}／
     *
     * @param array $names
     * @param int $updatedCount
     * @return string
     */
    private function buildStatusComment(array $names, int $updatedCount): string
    {
        return sprintf(
            '%s／%s／%s／%s／現在契約台数（更新後）：%d／',
            $names['park_name'],
            $names['ptype_subject'],
            $names['psection_subject'],
            $names['zone_name'],
            $updatedCount
        );
    }


    /**
     * エラー結果作成（SHJ-8ログも作成）
     *
     * @param int $errorCode
     * @param string $errorMessage
     * @param string $stackTrace
     * @return array
     */
    private function createErrorResult(int $errorCode, string $errorMessage, string $stackTrace = ''): array
    {
        // エラー時もSHJ-8でバッチログを作成
        try {
            $device = Device::orderBy('device_id')->first();
            $deviceId = $device ? $device->device_id : 1;
            $today = now()->format('Y/m/d');

            $statusComment = sprintf(
                'エラーコード：%d／エラーメッセージ：%s',
                $errorCode,
                $errorMessage
            );

            $this->shjEightService->execute(
                $deviceId,
                'SHJ-13',
                'SHJ-13契約台数追加',
                'success',  // 仕様書：エラー時も"success"で記録
                $statusComment,
                $today,
                $today
            );

            Log::info('SHJ-13 エラー時バッチログ作成完了', [
                'error_code' => $errorCode,
                'status_comment' => $statusComment
            ]);
        } catch (\Exception $e) {
            Log::error('SHJ-13 エラー時バッチログ作成失敗', [
                'error' => $e->getMessage()
            ]);
        }

        return [
            'result' => 1,
            'error_code' => $errorCode,
            'error_message' => $errorMessage,
            'stack_trace' => $stackTrace,
        ];
    }
}
