<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\Device;
use App\Models\RegularContract;
use App\Models\User;
use App\Models\Park;
use App\Models\Ope;
use App\Services\ShjMailSendService;

/**
 * SHJ-12 未払い者通知処理サービス
 * 
 * 定期契約マスタより未払い者を取得し、通知処理またはオペレーターキュー追加を実行
 */
class ShjTwelveService
{
    /**
     * SHJメール送信サービス
     *
     * @var ShjMailSendService
     */
    protected $shjMailSendService;

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

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

    /**
     * 【処理1】定期契約マスタより未払い者を取得する
     * 
     * SQL条件:
     * - 解約フラグ = 0 (未解約)
     * - 授受フラグ = 0 (授受フラグOFF)
     * - 請求金額 > 0 (請求金額あり)
     *
     * @return array 未払い者リスト
     */
    public function getUnpaidUsers(): array
    {
        try {
            $query = DB::table('regular_contract as T1')
                ->select([
                    'T1.contract_id',                // 定期契約ID
                    'T2.user_seq',                   // 利用者ID
                    'T2.user_name',                  // 利用者名
                    'T2.user_manual_regist_flag',    // 手動登録フラグ
                    'T2.user_primemail',             // メールアドレス
                    'T2.user_submail',               // 予備メールアドレス
                    'T1.park_id',                    // 駐輪場ID (regular_contractテーブルから)
                    'T3.park_name',                  // 駐輪場名
                    'T1.billing_amount'              // 請求金額
                ])
                ->join('user as T2', 'T1.user_id', '=', 'T2.user_seq')
                ->join('park as T3', 'T1.park_id', '=', 'T3.park_id')
                ->where([
                    ['T1.contract_cancel_flag', '=', 0],  // 解約フラグ = 0
                    ['T1.contract_flag', '=', 0],         // 授受フラグ = 0
                ])
                ->where('T1.billing_amount', '>', 0)      // 請求金額 > 0
                ->get();

            Log::info('SHJ-12 未払い者取得完了', [
                'count' => $query->count(),
                'sql_conditions' => [
                    'contract_cancel_flag' => 0,
                    'contract_flag' => 0,
                    'billing_amount' => '> 0'
                ]
            ]);

            return $query->toArray();

        } catch (\Exception $e) {
            Log::error('SHJ-12 未払い者取得エラー', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            throw $e;
        }
    }

    /**
     * 【処理2】未払い者への通知、またはオペレーターキュー追加処理
     *
     * 仕様:
     * - 手動登録フラグ = 0 (Web申込) → SHJ-7メール送信のみ
     * - 手動登録フラグ ≠ 0 (その他) → オペレーターキュー追加のみ
     *
     * @param array $unpaidUsers 未払い者リスト
     * @return array 処理結果
     */
    public function processUnpaidUserNotifications(array $unpaidUsers): array
    {
        // 内部変数（カウンタ）初期化
        $mailSuccessCount = 0;
        $mailErrorCount = 0;
        $queueSuccessCount = 0;
        $queueErrorCount = 0;
        $batchComments = [];  // バッチコメント（エラー情報）累積用
        $processParameters = [];

        try {
            DB::beginTransaction();

            // 処理1の取得レコード数分繰り返し
            foreach ($unpaidUsers as $user) {
                try {
                    // 【判定】手動登録フラグによる分岐
                    if ($user->user_manual_regist_flag == 0) {
                        // Web申込 → SHJ-7メール送信
                        $mailResult = $this->sendNotificationMailViaShj7($user);

                        // 処理結果判定: result === 0 → 正常
                        if (($mailResult['result'] ?? 1) === 0) {
                            $mailSuccessCount++;
                        } else {
                            $mailErrorCount++;
                            // バッチコメントにSHJ-7異常情報を設定（後ろに足す）
                            $batchComments[] = sprintf(
                                'SHJ-7メール送信: 契約ID:%s メール送信失敗: %s',
                                $user->contract_id,
                                $mailResult['error_info'] ?? 'Unknown error'
                            );
                        }

                        // 処理パラメータ記録
                        $processParameters[] = [
                            'contract_id' => $user->contract_id,
                            'user_seq' => $user->user_seq,
                            'billing_amount' => $user->billing_amount,
                            'process_type' => 'mail',
                            'result' => ($mailResult['result'] ?? 1) === 0 ? 'success' : 'error'
                        ];

                    } else {
                        // その他（手動登録）→ オペレーターキュー追加
                        $queueResult = $this->addToOperatorQueue($user);

                        if ($queueResult['success']) {
                            $queueSuccessCount++;
                        } else {
                            $queueErrorCount++;
                            // バッチコメントに異常情報を設定（後ろに足す）
                            $batchComments[] = sprintf(
                                'キュー登録: 契約ID:%s キュー登録失敗: %s',
                                $user->contract_id,
                                $queueResult['message'] ?? 'Unknown error'
                            );
                        }

                        // 処理パラメータ記録
                        $processParameters[] = [
                            'contract_id' => $user->contract_id,
                            'user_seq' => $user->user_seq,
                            'billing_amount' => $user->billing_amount,
                            'process_type' => 'queue',
                            'result' => $queueResult['success'] ? 'success' : 'error'
                        ];
                    }

                } catch (\Exception $e) {
                    // 個別エラーを記録して次の繰り返し処理へ
                    $batchComments[] = sprintf(
                        '契約ID:%s 処理エラー: %s',
                        $user->contract_id,
                        $e->getMessage()
                    );

                    Log::warning('SHJ-12 個別処理エラー', [
                        'contract_id' => $user->contract_id,
                        'user_seq' => $user->user_seq,
                        'error' => $e->getMessage()
                    ]);
                }
            }

            DB::commit();

            // ステータスコメント構築
            $statusComment = $this->buildStatusComment(
                $mailSuccessCount,
                $mailErrorCount,
                $queueSuccessCount,
                $queueErrorCount
            );

            // バッチコメント整形（最大100件、超過分は省略）
            $formattedBatchComments = $this->formatBatchComments($batchComments);

            return [
                'success' => true,
                'mail_success_count' => $mailSuccessCount,
                'mail_error_count' => $mailErrorCount,
                'queue_success_count' => $queueSuccessCount,
                'queue_error_count' => $queueErrorCount,
                'status_comment' => $statusComment,
                'batch_comments' => $formattedBatchComments,
                'parameters' => $processParameters,
                'message' => '未払い者通知処理完了'
            ];

        } catch (\Exception $e) {
            DB::rollBack();

            Log::error('SHJ-12 通知処理全体エラー', [
                'error' => $e->getMessage(),
                'processed_count' => count($processParameters)
            ]);

            return [
                'success' => false,
                'mail_success_count' => $mailSuccessCount,
                'mail_error_count' => $mailErrorCount,
                'queue_success_count' => $queueSuccessCount,
                'queue_error_count' => $queueErrorCount,
                'status_comment' => 'システムエラー',
                'batch_comments' => $batchComments,
                'parameters' => $processParameters,
                'message' => '通知処理エラー: ' . $e->getMessage(),
                'details' => $e->getTraceAsString()
            ];
        }
    }

    /**
     * SHJ-7 メール送信サービス経由でメール通知送信
     *
     * 仕様書パラメータ:
     * - メールアドレス: 処理1.メールアドレス
     * - 予備メールアドレス: 処理1.予備メールアドレス
     * - 使用プログラムID: 204
     *
     * @param object $user 未払い者情報
     * @return array SHJ-7の処理結果 (result_code: 0=正常, 1=異常)
     */
    private function sendNotificationMailViaShj7($user): array
    {
        try {
            // SHJ-7 executeMailSend 呼び出し
            $result = $this->shjMailSendService->executeMailSend(
                (string)($user->user_primemail ?? ''),  // メールアドレス
                (string)($user->user_submail ?? ''),    // 予備メールアドレス
                204                                       // 使用プログラムID
            );

            Log::info('SHJ-12 SHJ-7メール送信結果', [
                'contract_id' => $user->contract_id,
                'user_seq' => $user->user_seq,
                'result' => $result['result'] ?? 1,
                'error_info' => $result['error_info'] ?? ''
            ]);

            return $result;

        } catch (\Exception $e) {
            Log::error('SHJ-12 SHJ-7メール送信エラー', [
                'contract_id' => $user->contract_id,
                'error' => $e->getMessage()
            ]);

            return [
                'result' => 1,  // 異常終了
                'error_info' => 'SHJ-7メール送信エラー: ' . $e->getMessage()
            ];
        }
    }

    /**
     * オペレーターキューへの追加
     *
     * 仕様書SQL定義に基づき operator_que テーブルに登録
     * - キュー種別ID: 8 (支払い催促)
     * - キューステータスID: 1 (キュー発生)
     * - que_id: MAX+1方式で生成（db_now.sqlは非AUTO_INCREMENT）
     * - 主キー衝突時に1回リトライ
     *
     * @param object $user 未払い者情報
     * @return array 追加結果
     */
    private function addToOperatorQueue($user): array
    {
        $maxRetries = 2; // 最大2回試行（初回 + リトライ1回）
        $lastException = null;

        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            try {
                // オペレーターキューデータ構築
                // 注: que_idはAUTO_INCREMENTのため、DBに委任（手動採番しない）
                $queueData = [
                    // 'que_id' は削除（AUTO_INCREMENTに委任）
                    'que_class' => 8,                        // キュー種別ID: 8=支払い催促
                    'user_id' => $user->user_seq,            // 利用者ID
                    'contract_id' => $user->contract_id,     // 定期契約ID
                    'park_id' => $user->park_id,             // 駐輪場ID
                    'que_comment' => '',                     // キューコメント (空文字)
                    'que_status' => 1,                       // キューステータスID: 1=キュー発生
                    'que_status_comment' => '',              // キューステータスコメント (空文字)
                    'work_instructions' => '',               // 業務指示コメント (空文字)
                    'created_at' => now(),                   // 登録日時
                    'updated_at' => now(),                   // 更新日時
                    'operator_id' => null                    // 更新オペレータID (NULL: システム自動)
                ];

                // operator_queテーブルに挿入（que_idはDBが自動採番）
                $newQueId = DB::table('operator_que')->insertGetId($queueData);

                Log::info('SHJ-12 オペレーターキュー追加完了', [
                    'que_id' => $newQueId,
                    'contract_id' => $user->contract_id,
                    'user_seq' => $user->user_seq,
                    'park_id' => $user->park_id,
                    'que_class' => 8,
                    'que_status' => 1,
                    'attempt' => $attempt
                ]);

                return [
                    'success' => true,
                    'message' => 'オペレーターキュー追加成功',
                    'que_id' => $newQueId
                ];

            } catch (\Illuminate\Database\QueryException $e) {
                $lastException = $e;

                // 主キー重複エラー（Duplicate entry）の場合のみリトライ
                // SQLSTATEコード 23000 は整合性制約違反を示す
                if ($e->getCode() == 23000 && $attempt < $maxRetries) {
                    Log::warning('SHJ-12 que_id重複検出、リトライします', [
                        'attempt' => $attempt,
                        'max_retries' => $maxRetries,
                        'que_id' => $newQueId ?? null,
                        'contract_id' => $user->contract_id,
                        'error_code' => $e->getCode()
                    ]);

                    // 短時間待機後に再試行（100ms）
                    usleep(100000);
                    continue;
                }

                // その他のエラー、または最終試行での失敗時は例外をスロー
                throw $e;

            } catch (\Exception $e) {
                // QueryException以外の例外は即座にスロー
                $lastException = $e;
                throw $e;
            }
        }

        // ループを抜けた場合（通常は到達しない）
        Log::error('SHJ-12 オペレーターキュー追加エラー（最大リトライ回数超過）', [
            'contract_id' => $user->contract_id,
            'user_seq' => $user->user_seq,
            'max_retries' => $maxRetries,
            'error' => $lastException ? $lastException->getMessage() : 'Unknown error',
            'trace' => $lastException ? $lastException->getTraceAsString() : ''
        ]);

        return [
            'success' => false,
            'message' => 'オペレーターキュー追加エラー（リトライ失敗）: ' .
                         ($lastException ? $lastException->getMessage() : 'Unknown error')
        ];
    }

    /**
     * ステータスコメント構築
     *
     * 仕様書に基づくステータスコメント形式:
     * - エラーなし: "メール送信成功:X件 / キュー登録成功:Y件"
     * - エラーあり: "メール送信成功:X件 / メール送信失敗:A件 / キュー登録成功:Y件 / キュー登録失敗:B件"
     *
     * @param int $mailSuccessCount メール正常終了件数
     * @param int $mailErrorCount メール異常終了件数
     * @param int $queueSuccessCount キュー登録正常終了件数
     * @param int $queueErrorCount キュー登録異常終了件数
     * @return string ステータスコメント
     */
    private function buildStatusComment(
        int $mailSuccessCount,
        int $mailErrorCount,
        int $queueSuccessCount,
        int $queueErrorCount
    ): string {
        $parts = [];

        // メール送信結果
        if ($mailSuccessCount > 0 || $mailErrorCount > 0) {
            if ($mailErrorCount > 0) {
                $parts[] = "メール送信成功:{$mailSuccessCount}件";
                $parts[] = "メール送信失敗:{$mailErrorCount}件";
            } else {
                $parts[] = "メール送信成功:{$mailSuccessCount}件";
            }
        }

        // キュー登録結果
        if ($queueSuccessCount > 0 || $queueErrorCount > 0) {
            if ($queueErrorCount > 0) {
                $parts[] = "キュー登録成功:{$queueSuccessCount}件";
                $parts[] = "キュー登録失敗:{$queueErrorCount}件";
            } else {
                $parts[] = "キュー登録成功:{$queueSuccessCount}件";
            }
        }

        return implode(' / ', $parts);
    }

    /**
     * バッチコメント整形
     *
     * エラー情報を最大100件に制限し、超過分は省略表記
     *
     * @param array $batchComments エラー情報配列
     * @return string 整形済みバッチコメント
     */
    private function formatBatchComments(array $batchComments): string
    {
        if (empty($batchComments)) {
            return '';
        }

        // 最大100件に制限
        $limitedComments = array_slice($batchComments, 0, 100);

        // 超過分の件数を追加
        if (count($batchComments) > 100) {
            $limitedComments[] = sprintf(
                '...他%d件省略',
                count($batchComments) - 100
            );
        }

        return implode("\n", $limitedComments);
    }

    /**
     * 【処理3】バッチ処理ログを作成する
     *
     * SHJ-8サービスを呼び出してbat_job_logテーブルに登録（業務固有のstatus_comment記録）
     *
     * @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 {
            // デバイスIDを取得
            $device = Device::orderBy('device_id')->first();
            $deviceId = $device ? $device->device_id : 1;

            // status_commentを構築（業務情報を含む）
            $statusComment = sprintf(
                '%s (実行:%d/成功:%d/エラー:%d)',
                $message,
                $executionCount,
                $successCount,
                $errorCount
            );

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

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

            // SHJ-8サービスを呼び出し
            $this->shjEightService->execute(
                $deviceId,
                'SHJ-12',
                'SHJ-12未払い者通知',
                $status,
                $statusComment,
                $today,
                $today
            );

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