<?php

namespace App\Services;

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

/**
 * SHJ-8 バッチ処理ログ作成サービス
 *
 * 概要: 入力パラメーターの情報を元にバッチ処理ログ情報を作成する
 *
 * 処理フロー:
 * 【処理1】入力パラメーターをチェックする
 * 【判断1】チェック結果
 *   NG → パラメーターNGの結果を設定する → 【処理3】
 *   OK → 【処理2】
 * 【処理2】バッチ処理ログを登録する
 * 【処理3】処理結果を返却する
 */
class ShjEightService
{
    /**
     * SHJ-8 メイン処理実行
     *
     * 修正版：7項目入力（status_comment追加）
     * @param int $deviceId デバイスID (必須)
     * @param string|null $processName プロセス名
     * @param string|null $jobName ジョブ名
     * @param string $status ステータス
     * @param string $statusComment ステータスコメント (必須, ≤255文字)
     * @param string $createdDate 登録日時 (yyyy/mm/dd形式)
     * @param string $updatedDate 更新日時 (yyyy/mm/dd形式)
     * @return array 処理結果 ['result' => 0|1, 'error_message' => string|null]
     */
    public function execute(
        int $deviceId,
        ?string $processName,
        ?string $jobName,
        string $status,
        string $statusComment,
        string $createdDate,
        string $updatedDate
    ): array {
        try {
            Log::info('SHJ-8 バッチ処理ログ作成開始', [
                'device_id' => $deviceId,
                'process_name' => $processName,
                'job_name' => $jobName,
                'status' => $status,
                'status_comment' => $statusComment,
                'created_date' => $createdDate,
                'updated_date' => $updatedDate
            ]);

            // 【処理1】入力パラメーターをチェックする
            $validationResult = $this->validateParameters(
                $deviceId,
                $processName,
                $jobName,
                $status,
                $statusComment,
                $createdDate,
                $updatedDate
            );

            // 【判断1】チェック結果
            if (!$validationResult['valid']) {
                // パラメーターNG
                $errorMessage = $validationResult['error_message'];

                Log::warning('SHJ-8 パラメーターチェックNG', [
                    'error_message' => $errorMessage
                ]);

                // 【処理3】異常終了の結果を返却
                return [
                    'result' => 1,
                    'error_message' => $errorMessage
                ];
            }

            // 【処理2】バッチ処理ログを登録する
            $batJobLog = $this->createBatchJobLog(
                $deviceId,
                $processName,
                $jobName,
                $status,
                $statusComment,
                $createdDate,
                $updatedDate
            );

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

            // 【処理3】正常終了の結果を返却
            return [
                'result' => 0
            ];

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

            // 例外発生時の処理結果を返却
            return [
                'result' => 1,
                'error_code' => $e->getCode(),
                'error_message' => $e->getMessage(),
                'stack_trace' => $e->getTraceAsString()
            ];
        }
    }

    /**
     * 【処理1】入力パラメーターをチェックする
     *
     * 修正版：7項目チェック（status_comment追加）
     * 1. デバイスID: 必須、device表に存在チェック
     * 2. プロセス名: 「プロセス名」「ジョブ名」いずれか必須
     * 3. ジョブ名: 「プロセス名」「ジョブ名」いずれか必須
     * 4. ステータス: 必須
     * 5. ステータスコメント: 必須、≤255文字
     * 6. 登録日時: 日付型(yyyy/mm/dd形式)
     * 7. 更新日時: 日付型(yyyy/mm/dd形式)
     *
     * @param int $deviceId デバイスID
     * @param string|null $processName プロセス名
     * @param string|null $jobName ジョブ名
     * @param string $status ステータス
     * @param string $statusComment ステータスコメント
     * @param string $createdDate 登録日時
     * @param string $updatedDate 更新日時
     * @return array 検証結果 ['valid' => bool, 'error_message' => string|null]
     */
    private function validateParameters(
        int $deviceId,
        ?string $processName,
        ?string $jobName,
        string $status,
        string $statusComment,
        string $createdDate,
        string $updatedDate
    ): array {
        $errors = [];

        // 1. デバイスIDチェック (必須、存在チェック)
        if ($deviceId <= 0) {
            $errors[] = "パラメーターNG：デバイスID/{$deviceId}";
        } elseif (!Device::where('device_id', $deviceId)->exists()) {
            $errors[] = "パラメーターNG：デバイスID/{$deviceId}";
        }

        // 2. プロセス名とジョブ名のいずれか必須チェック
        if (empty($processName) && empty($jobName)) {
            $errors[] = "パラメーターNG：プロセス名/<空>";
            $errors[] = "パラメーターNG：ジョブ名/<空>";
        }

        // 3. ステータス必須チェック
        if (empty($status)) {
            $errors[] = "パラメーターNG：ステータス/<空>";
        }

        // 4. ステータスコメント必須チェック、255文字以内
        if (empty($statusComment)) {
            $errors[] = "パラメーターNG：ステータスコメント/<空>";
        } elseif (mb_strlen($statusComment) > 255) {
            $errors[] = "パラメーターNG：ステータスコメント/長さ超過(" . mb_strlen($statusComment) . "文字)";
        }

        // 5. 登録日時の日付型チェック
        if (!$this->isValidDateFormat($createdDate)) {
            $errors[] = "パラメーターNG：登録日時/{$createdDate}";
        }

        // 6. 更新日時の日付型チェック
        if (!$this->isValidDateFormat($updatedDate)) {
            $errors[] = "パラメーターNG：更新日時/{$updatedDate}";
        }

        // エラーがある場合
        if (!empty($errors)) {
            // 複数のエラーがある場合は全角カンマ（、）で連結
            $errorMessage = implode('、', $errors);

            return [
                'valid' => false,
                'error_message' => $errorMessage
            ];
        }

        // 正常終了
        return [
            'valid' => true,
            'error_message' => null
        ];
    }

    /**
     * 日付形式の検証
     *
     * yyyy/mm/dd形式かチェック
     *
     * @param string $date 日付文字列
     * @return bool 有効な日付形式かどうか
     */
    private function isValidDateFormat(string $date): bool
    {
        // yyyy/mm/dd形式の正規表現チェック
        if (!preg_match('/^\d{4}\/\d{2}\/\d{2}$/', $date)) {
            return false;
        }

        // 実際の日付として有効かチェック
        $dateParts = explode('/', $date);
        return checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0]);
    }

    /**
     * 【処理2】バッチ処理ログを登録する
     *
     * bat_job_logテーブルにINSERT
     * 修正版：ステータスコメントは呼び出し元から受け取る（固定値廃止）
     *
     * @param int $deviceId デバイスID
     * @param string|null $processName プロセス名
     * @param string|null $jobName ジョブ名
     * @param string $status ステータス
     * @param string $statusComment ステータスコメント（業務固有）
     * @param string $createdDate 登録日時 (yyyy/mm/dd形式)
     * @param string $updatedDate 更新日時 (yyyy/mm/dd形式)
     * @return BatJobLog 作成されたバッチジョブログ
     */
    private function createBatchJobLog(
        int $deviceId,
        ?string $processName,
        ?string $jobName,
        string $status,
        string $statusComment,
        string $createdDate,
        string $updatedDate
    ): BatJobLog {
        // 日付文字列をdatetime型に変換（現在時刻を使用）
        $createdDatetime = Carbon::createFromFormat('Y/m/d', $createdDate);
        $updatedDatetime = Carbon::createFromFormat('Y/m/d', $updatedDate);

        // bat_job_logテーブルに登録（status_commentは呼び出し元から受け取った値を使用）
        $batJobLog = BatJobLog::create([
            'device_id' => $deviceId,
            'process_name' => $processName,
            'job_name' => $jobName,
            'status' => $status,
            'status_comment' => $statusComment,
            'created_at' => $createdDatetime,
            'updated_at' => $updatedDatetime
        ]);

        return $batJobLog;
    }
}
