<?php

namespace App\Services;

use App\Models\Device;
use App\Models\HardwareCheckLog;
use App\Models\PrintJobLog;
use App\Models\Batch\BatchLog;
use App\Models\OperatorQue;
use App\Models\User;
use App\Models\Park;
use App\Services\ShjMailSendService;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

/**
 * SHJ-6 サーバ死活監視処理サービス
 * 
 * サーバとデバイスの死活監視、ハードウェア状態監視、プリンタログ監視を実行
 * 異常検出時のメール通知機能を含む統合モニタリングサービス
 */
class ShjSixService
{
    /**
     * Device モデル
     *
     * @var Device
     */
    protected $deviceModel;

    /**
     * HardwareCheckLog モデル
     *
     * @var HardwareCheckLog
     */
    protected $hardwareCheckLogModel;

    /**
     * PrintJobLog モデル
     *
     * @var PrintJobLog
     */
    protected $printJobLogModel;

    /**
     * BatchLog モデル
     *
     * @var BatchLog
     */
    protected $batchLogModel;

    /**
     * OperatorQue モデル
     *
     * @var OperatorQue
     */
    protected $operatorQueModel;

    /**
     * ShjMailSendService
     *
     * @var ShjMailSendService
     */
    protected $mailSendService;

    /**
     * 固定メールアドレス（DB反映NG時用）
     *
     * @var string
     */
    const FIXED_EMAIL_ADDRESS = 'system-alert@so-manager.com';

    /**
     * プリンタログ監視期間（分）
     *
     * @var int
     */
    const PRINTER_LOG_MONITOR_MINUTES = 15;

    /**
     * コンストラクタ
     *
     * @param Device $deviceModel
     * @param HardwareCheckLog $hardwareCheckLogModel
     * @param PrintJobLog $printJobLogModel
     * @param BatchLog $batchLogModel
     * @param OperatorQue $operatorQueModel
     * @param ShjMailSendService $mailSendService
     */
    public function __construct(
        Device $deviceModel,
        HardwareCheckLog $hardwareCheckLogModel,
        PrintJobLog $printJobLogModel,
        BatchLog $batchLogModel,
        OperatorQue $operatorQueModel,
        ShjMailSendService $mailSendService
    ) {
        $this->deviceModel = $deviceModel;
        $this->hardwareCheckLogModel = $hardwareCheckLogModel;
        $this->printJobLogModel = $printJobLogModel;
        $this->batchLogModel = $batchLogModel;
        $this->operatorQueModel = $operatorQueModel;
        $this->mailSendService = $mailSendService;
    }

    /**
     * SHJ-6 サーバ死活監視処理メイン実行
     * 
     * 処理フロー:
     * 【処理1】サーバ死活監視（DBアクセス）
     * 【処理2】デバイス管理マスタを取得する
     * 【処理3】デバイス毎のハードウェア状態を取得する
     * 【処理4】プリンタ制御プログラムログを取得する
     * 【判断3】エラーログ有無
     * 【処理5】バッチ処理ログを作成する
     * ※ 異常時は共通A処理を実行
     *
     * @return array 処理結果
     */
    public function executeServerMonitoring(): array
    {
        $batchLogId = null;
        $warnings = [];
        $errorDetails = [];
        
        try {
            // バッチ処理開始ログ作成
            $batchLog = BatchLog::createBatchLog(
                'shj6',
                BatchLog::STATUS_START,
                [],
                'SHJ-6 サーバ死活監視処理開始'
            );
            $batchLogId = $batchLog->id;

            Log::info('SHJ-6 サーバ死活監視処理開始', [
                'batch_log_id' => $batchLogId
            ]);

            // 【処理1】サーバ死活監視（DBアクセス）
            $dbAccessResult = $this->checkDatabaseAccess();
            if (!$dbAccessResult['success']) {
                // DB接続NGの場合は共通A処理実行
                $this->executeCommonProcessA($batchLogId, 'DB接続エラー: ' . $dbAccessResult['message']);
                
                return [
                    'success' => false,
                    'message' => 'データベース接続エラーが発生しました',
                    'error_details' => [$dbAccessResult['message']],
                    'batch_log_id' => $batchLogId
                ];
            }

            // 【処理2】デバイス管理マスタを取得する
            $devices = $this->getDeviceManagementData();
            Log::info('デバイス管理マスタ取得完了', [
                'device_count' => count($devices)
            ]);

            // 【処理3】デバイス毎のハードウェア状態を取得する
            $hardwareStatusResult = $this->getHardwareStatus($devices);
            if (!$hardwareStatusResult['success']) {
                // ハードウェア状態取得できなかった場合は共通A処理実行
                $this->executeCommonProcessA($batchLogId, 'ハードウェア状態取得エラー: ' . $hardwareStatusResult['message']);
                $warnings[] = 'ハードウェア状態の一部で異常を検出しました';
                $errorDetails[] = $hardwareStatusResult['message'];
            }

            // 【処理4】プリンタ制御プログラムログを取得する
            $printerLogResult = $this->getPrinterControlLogs();
            
            // 【判断3】エラーログ有無
            if ($printerLogResult['has_errors']) {
                // エラーログ有の場合は共通A処理実行
                $this->executeCommonProcessA($batchLogId, 'プリンタエラーログ検出: ' . $printerLogResult['error_summary']);
                $warnings[] = 'プリンタ制御でエラーが検出されました';
                $errorDetails[] = $printerLogResult['error_summary'];
            }

            // 【処理5】バッチ処理ログを作成する
            $monitoringSummary = $this->createMonitoringSummary($devices, $hardwareStatusResult, $printerLogResult);
            
            $status = empty($warnings) ? BatchLog::STATUS_SUCCESS : BatchLog::STATUS_WARNING;
            $message = empty($warnings) ? 
                'SHJ-6 サーバ死活監視処理正常完了' : 
                'SHJ-6 サーバ死活監視処理完了（警告あり）';

            $batchLog->update([
                'status' => $status,
                'end_time' => now(),
                'message' => $message,
                'success_count' => 1
            ]);

            Log::info('SHJ-6 サーバ死活監視処理完了', [
                'batch_log_id' => $batchLogId,
                'monitoring_summary' => $monitoringSummary,
                'warnings' => $warnings
            ]);

            return [
                'success' => true,
                'message' => 'SHJ-6 サーバ死活監視処理が完了しました',
                'monitoring_summary' => $monitoringSummary,
                'warnings' => $warnings,
                'error_details' => $errorDetails,
                'batch_log_id' => $batchLogId
            ];

        } catch (\Exception $e) {
            $errorMessage = 'SHJ-6 サーバ死活監視処理でエラーが発生: ' . $e->getMessage();
            
            if (isset($batchLog) && $batchLog) {
                $batchLog->update([
                    'status' => BatchLog::STATUS_ERROR,
                    'end_time' => now(),
                    'message' => $errorMessage,
                    'error_details' => $e->getMessage(),
                    'error_count' => 1
                ]);
            }

            // 例外発生時も共通A処理実行
            $this->executeCommonProcessA($batchLogId, $errorMessage);

            Log::error('SHJ-6 サーバ死活監視処理エラー', [
                'batch_log_id' => $batchLogId,
                'exception' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

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

    /**
     * 【処理1】サーバ死活監視（DBアクセス）
     * 
     * @return array アクセス結果
     */
    private function checkDatabaseAccess(): array
    {
        try {
            // 設定マスタテーブルへの簡単なクエリでDB接続確認
            $result = DB::select('SELECT 1 as test');
            
            if (empty($result)) {
                return [
                    'success' => false,
                    'message' => 'データベースクエリの結果が空です'
                ];
            }
            
            Log::info('データベース接続確認成功');
            
            return [
                'success' => true,
                'message' => 'データベース接続正常'
            ];
            
        } catch (\Exception $e) {
            Log::error('データベース接続エラー', [
                'error' => $e->getMessage()
            ]);
            
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }

    /**
     * 【処理2】デバイス管理マスタを取得する
     * 
     * @return array デバイス情報
     */
    private function getDeviceManagementData(): array
    {
        try {
            $devices = DB::table('device')
                ->select([
                    'device_id',
                    'park_id',
                    'device_type',
                    'device_subject',
                    'device_identifier',
                    'device_work',
                    'device_workstart',
                    'device_replace',
                    'device_remarks',
                    'operator_id'
                ])
                ->where('device_workstart', '<=', now())
                ->whereNull('device_replace')
                ->get()
                ->toArray();

            Log::info('デバイス管理マスタ取得完了', [
                'device_count' => count($devices)
            ]);

            return $devices;

        } catch (\Exception $e) {
            Log::error('デバイス管理マスタ取得エラー', [
                'error' => $e->getMessage()
            ]);

            throw $e;
        }
    }

    /**
     * 【処理3】デバイス毎のハードウェア状態を取得する
     * 
     * @param array $devices デバイス一覧
     * @return array ハードウェア状態結果
     */
    private function getHardwareStatus(array $devices): array
    {
        try {
            $normalDevices = 0;
            $abnormalDevices = 0;
            $abnormalDetails = [];
            
            foreach ($devices as $device) {
                $latestStatus = HardwareCheckLog::getLatestStatusByDevice($device->device_id);
                
                if (!$latestStatus) {
                    $abnormalDevices++;
                    $abnormalDetails[] = "デバイスID {$device->device_id}: ハードウェア状態ログが存在しません";
                    continue;
                }
                
                if ($latestStatus->isNormal()) {
                    $normalDevices++;
                } else {
                    $abnormalDevices++;
                    $abnormalDetails[] = "デバイスID {$device->device_id}: {$latestStatus->getStatusNameAttribute()} - {$latestStatus->status_comment}";
                }
            }
            
            Log::info('ハードウェア状態取得完了', [
                'total_devices' => count($devices),
                'normal_devices' => $normalDevices,
                'abnormal_devices' => $abnormalDevices
            ]);
            
            return [
                'success' => $abnormalDevices === 0,
                'total_devices' => count($devices),
                'normal_devices' => $normalDevices,
                'abnormal_devices' => $abnormalDevices,
                'abnormal_details' => $abnormalDetails,
                'message' => $abnormalDevices > 0 ? 
                    "{$abnormalDevices}台のデバイスで異常を検出" : 
                    '全デバイス正常'
            ];
            
        } catch (\Exception $e) {
            Log::error('ハードウェア状態取得エラー', [
                'error' => $e->getMessage()
            ]);
            
            return [
                'success' => false,
                'message' => $e->getMessage(),
                'abnormal_details' => ["ハードウェア状態取得中にエラーが発生: " . $e->getMessage()]
            ];
        }
    }

    /**
     * 【処理4】プリンタ制御プログラムログを取得する
     * 
     * @return array プリンタログ結果
     */
    private function getPrinterControlLogs(): array
    {
        try {
            // 過去15分間のエラーログを取得
            $errorLogs = PrintJobLog::getRecentErrorLogs();
            
            $hasErrors = $errorLogs->count() > 0;
            $errorSummary = '';
            $errorDetails = [];
            
            if ($hasErrors) {
                $errorsByCode = $errorLogs->groupBy('error_code');
                $errorSummaryParts = [];
                
                foreach ($errorsByCode as $errorCode => $logs) {
                    $count = $logs->count();
                    $errorSummaryParts[] = "エラーコード{$errorCode}: {$count}件";
                    
                    foreach ($logs as $log) {
                        $errorDetails[] = sprintf(
                            "[%s] %s - エラーコード: %d, %s",
                            $log->created_at->format('Y-m-d H:i:s'),
                            $log->process_name,
                            $log->error_code,
                            $log->status_comment
                        );
                    }
                }
                
                $errorSummary = implode(', ', $errorSummaryParts);
            }
            
            Log::info('プリンタ制御プログラムログ取得完了', [
                'monitoring_period_minutes' => self::PRINTER_LOG_MONITOR_MINUTES,
                'error_logs_count' => $errorLogs->count(),
                'has_errors' => $hasErrors
            ]);
            
            return [
                'success' => true,
                'has_errors' => $hasErrors,
                'error_count' => $errorLogs->count(),
                'error_summary' => $errorSummary,
                'error_details' => $errorDetails,
                'monitoring_period' => self::PRINTER_LOG_MONITOR_MINUTES . '分間'
            ];
            
        } catch (\Exception $e) {
            Log::error('プリンタ制御プログラムログ取得エラー', [
                'error' => $e->getMessage()
            ]);
            
            return [
                'success' => false,
                'has_errors' => true,
                'error_summary' => 'ログ取得エラー: ' . $e->getMessage(),
                'error_details' => ["プリンタログ取得中にエラーが発生: " . $e->getMessage()]
            ];
        }
    }

    /**
     * 監視結果サマリーを作成
     * 
     * @param array $devices デバイス一覧
     * @param array $hardwareResult ハードウェア結果
     * @param array $printerResult プリンタ結果
     * @return string 監視サマリー
     */
    private function createMonitoringSummary(array $devices, array $hardwareResult, array $printerResult): string
    {
        $summary = [
            'デバイス数: ' . count($devices),
            'ハードウェア正常: ' . ($hardwareResult['normal_devices'] ?? 0) . '台',
            'ハードウェア異常: ' . ($hardwareResult['abnormal_devices'] ?? 0) . '台',
            'プリンタエラー: ' . ($printerResult['error_count'] ?? 0) . '件'
        ];
        
        return implode(', ', $summary);
    }

    /**
     * 共通A処理：監視結果を反映
     * 
     * @param int|null $batchLogId バッチログID
     * @param string $alertMessage アラートメッセージ
     * @return void
     */
    private function executeCommonProcessA(?int $batchLogId, string $alertMessage): void
    {
        try {
            Log::info('共通A処理開始', [
                'batch_log_id' => $batchLogId,
                'alert_message' => $alertMessage
            ]);

            // 【共通判断1】DB反映可否判定
            $canReflectToDb = $this->canReflectToDatabase();
            
            if ($canReflectToDb) {
                // 【共通処理1】オペレータキューを登録する
                $this->registerOperatorQueue($alertMessage, $batchLogId);
                
                // 【共通処理2】メール送信対象オペレータを取得する
                $operators = $this->getMailTargetOperators();
                
                // 【共通判断2】送信対象有無
                if (!empty($operators)) {
                    foreach ($operators as $operator) {
                        $this->sendAlertMail($operator['email'], $alertMessage, 'オペレータ');
                    }
                }
                
                // 【共通処理3】駐輪場管理者を取得する
                $parkManagers = $this->getParkManagers();
                
                // 【共通判断3】送信対象有無
                if (!empty($parkManagers)) {
                    foreach ($parkManagers as $manager) {
                        $this->sendAlertMail($manager['email'], $alertMessage, '駐輪場管理者');
                    }
                }
                
            } else {
                // DB反映NGの場合は固定メールアドレスに送信
                $this->sendAlertMail(self::FIXED_EMAIL_ADDRESS, $alertMessage, 'システム管理者');
            }
            
            Log::info('共通A処理完了', [
                'batch_log_id' => $batchLogId
            ]);
            
        } catch (\Exception $e) {
            Log::error('共通A処理エラー', [
                'batch_log_id' => $batchLogId,
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * DB反映可否判定
     * 
     * @return bool 反映可能かどうか
     */
    private function canReflectToDatabase(): bool
    {
        try {
            // 簡単なINSERTテストでDB反映可否を確認
            DB::beginTransaction();
            $testId = DB::table('operator_que')->insertGetId([
                'que_class' => 6,
                'que_comment' => 'DB反映テスト',
                'que_status' => 0,
                'created_at' => now(),
                'updated_at' => now()
            ]);
            
            // テストレコードを削除
            DB::table('operator_que')->where('que_id', $testId)->delete();
            DB::commit();
            
            return true;
            
        } catch (\Exception $e) {
            DB::rollBack();
            Log::warning('DB反映不可', ['error' => $e->getMessage()]);
            return false;
        }
    }

    /**
     * オペレータキューを登録
     * 
     * @param string $alertMessage アラートメッセージ
     * @param int|null $batchLogId バッチログID
     * @return void
     */
    private function registerOperatorQueue(string $alertMessage, ?int $batchLogId): void
    {
        try {
            OperatorQue::create([
                'que_class' => 6, // SHJ-6用のクラス
                'user_id' => null,
                'contract_id' => null,
                'park_id' => null,
                'que_comment' => $alertMessage,
                'que_status' => 0, // 待機中
                'que_status_comment' => 'システム監視アラート',
                'work_instructions' => "SHJ-6監視処理 BatchLogID: {$batchLogId}",
                'created_at' => now(),
                'updated_at' => now()
            ]);
            
            Log::info('オペレータキュー登録完了', [
                'batch_log_id' => $batchLogId,
                'alert_message' => $alertMessage
            ]);
            
        } catch (\Exception $e) {
            Log::error('オペレータキュー登録エラー', [
                'batch_log_id' => $batchLogId,
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * メール送信対象オペレータを取得
     * 
     * @return array オペレータ一覧
     */
    private function getMailTargetOperators(): array
    {
        try {
            // user_typeがオペレータのユーザーを取得（仮の条件）
            $operators = DB::table('users')
                ->select(['user_id', 'email', 'name'])
                ->where('user_type', 'operator') // 実際のテーブル構造に合わせて調整
                ->whereNotNull('email')
                ->where('email', '!=', '')
                ->get()
                ->map(function ($user) {
                    return [
                        'user_id' => $user->user_id,
                        'email' => $user->email,
                        'name' => $user->name ?? 'オペレータ'
                    ];
                })
                ->toArray();
                
            Log::info('メール送信対象オペレータ取得完了', [
                'operator_count' => count($operators)
            ]);
            
            return $operators;
            
        } catch (\Exception $e) {
            Log::error('メール送信対象オペレータ取得エラー', [
                'error' => $e->getMessage()
            ]);
            
            return [];
        }
    }

    /**
     * 駐輪場管理者を取得
     * 
     * @return array 駐輪場管理者一覧
     */
    private function getParkManagers(): array
    {
        try {
            // 駐輪場管理者を取得（仮の条件）
            $managers = DB::table('users')
                ->select(['user_id', 'email', 'name'])
                ->where('user_type', 'park_manager') // 実際のテーブル構造に合わせて調整
                ->whereNotNull('email')
                ->where('email', '!=', '')
                ->get()
                ->map(function ($user) {
                    return [
                        'user_id' => $user->user_id,
                        'email' => $user->email,
                        'name' => $user->name ?? '駐輪場管理者'
                    ];
                })
                ->toArray();
                
            Log::info('駐輪場管理者取得完了', [
                'manager_count' => count($managers)
            ]);
            
            return $managers;
            
        } catch (\Exception $e) {
            Log::error('駐輪場管理者取得エラー', [
                'error' => $e->getMessage()
            ]);
            
            return [];
        }
    }

    /**
     * アラートメールを送信
     * 
     * @param string $email メールアドレス
     * @param string $alertMessage アラートメッセージ
     * @param string $recipientType 受信者タイプ
     * @return void
     */
    private function sendAlertMail(string $email, string $alertMessage, string $recipientType): void
    {
        try {
            // SHJメール送信機能を使用（メールテンプレートID=1を使用、実際の値に調整）
            $result = $this->mailSendService->executeMailSend(
                $email,
                '', // 予備メールアドレスは空
                1   // システムアラート用メールテンプレートID
            );
            
            if ($result['success']) {
                Log::info('アラートメール送信成功', [
                    'email' => $email,
                    'recipient_type' => $recipientType,
                    'alert_message' => $alertMessage
                ]);
            } else {
                Log::error('アラートメール送信失敗', [
                    'email' => $email,
                    'recipient_type' => $recipientType,
                    'error' => $result['message']
                ]);
            }
            
        } catch (\Exception $e) {
            Log::error('アラートメール送信エラー', [
                'email' => $email,
                'recipient_type' => $recipientType,
                'error' => $e->getMessage()
            ]);
        }
    }
}
