<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;

/**
 * プリンタジョブログモデル - print_job_logテーブル
 * 
 * プリンタ制御プログラムの実行ログを管理
 */
class PrintJobLog extends Model
{
    /**
     * テーブル名
     *
     * @var string
     */
    protected $table = 'print_job_log';

    /**
     * プライマリキー
     *
     * @var string
     */
    protected $primaryKey = 'job_log_id';

    /**
     * 一括代入可能な属性
     *
     * @var array
     */
    protected $fillable = [
        'park_id',            // 駐輪場ID
        'user_id',            // ユーザーID
        'contract_id',        // 契約ID
        'process_name',       // プロセス名
        'job_name',           // ジョブ名
        'status',             // ステータス
        'error_code',         // エラーコード
        'status_comment',     // ステータスコメント
        'created_at'          // 作成日時
    ];

    /**
     * キャストする属性
     *
     * @var array
     */
    protected $casts = [
        'job_log_id' => 'integer',
        'park_id' => 'integer',
        'user_id' => 'integer',
        'contract_id' => 'integer',
        'error_code' => 'integer',
        'created_at' => 'datetime'
    ];

    /**
     * エラーコードの定数
     */
    const ERROR_CODE_THRESHOLD = 100;    // エラー判定閾値（>=100がエラー）

    /**
     * updateされない設定（created_atのみ）
     *
     * @var bool
     */
    public $timestamps = false;

    /**
     * 駐輪場との関連
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function park()
    {
        return $this->belongsTo(Park::class, 'park_id', 'park_id');
    }

    /**
     * ユーザーとの関連
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'user_id');
    }

    /**
     * 契約との関連
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function contract()
    {
        return $this->belongsTo(RegularContract::class, 'contract_id', 'contract_id');
    }

    /**
     * 過去15分間のエラーログを取得
     * 
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public static function getRecentErrorLogs()
    {
        $fifteenMinutesAgo = Carbon::now()->subMinutes(15);
        
        return self::where('created_at', '>=', $fifteenMinutesAgo)
            ->where('error_code', '>=', self::ERROR_CODE_THRESHOLD)
            ->orderBy('created_at', 'desc')
            ->get();
    }

    /**
     * 指定期間内のエラーログを取得
     * 
     * @param string $startTime 開始時刻
     * @param string $endTime 終了時刻
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public static function getErrorLogsByPeriod(string $startTime, string $endTime)
    {
        return self::whereBetween('created_at', [$startTime, $endTime])
            ->where('error_code', '>=', self::ERROR_CODE_THRESHOLD)
            ->orderBy('created_at', 'desc')
            ->get();
    }

    /**
     * 指定期間内の全ログを取得
     * 
     * @param string $startTime 開始時刻
     * @param string $endTime 終了時刻
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public static function getLogsByPeriod(string $startTime, string $endTime)
    {
        return self::whereBetween('created_at', [$startTime, $endTime])
            ->orderBy('created_at', 'desc')
            ->get();
    }

    /**
     * プリンタジョブログを作成
     * 
     * @param array $data ログデータ
     * @return PrintJobLog 作成されたログ
     */
    public static function createLog(array $data): PrintJobLog
    {
        $defaultData = [
            'park_id' => null,
            'user_id' => null,
            'contract_id' => null,
            'process_name' => '',
            'job_name' => '',
            'status' => '',
            'error_code' => 0,
            'status_comment' => '',
            'created_at' => now()
        ];
        
        $mergedData = array_merge($defaultData, $data);
        
        return self::create($mergedData);
    }

    /**
     * エラーログかどうかを判定
     * 
     * @return bool エラーログかどうか
     */
    public function isError(): bool
    {
        return $this->error_code >= self::ERROR_CODE_THRESHOLD;
    }

    /**
     * 正常ログかどうかを判定
     * 
     * @return bool 正常ログかどうか
     */
    public function isNormal(): bool
    {
        return $this->error_code < self::ERROR_CODE_THRESHOLD;
    }

    /**
     * エラーレベルを取得
     * 
     * @return string エラーレベル
     */
    public function getErrorLevel(): string
    {
        if ($this->error_code >= 200) {
            return '重大エラー';
        } elseif ($this->error_code >= self::ERROR_CODE_THRESHOLD) {
            return 'エラー';
        } else {
            return '正常';
        }
    }

    /**
     * エラーログの統計情報を取得
     * 
     * @param string $startTime 開始時刻
     * @param string $endTime 終了時刻
     * @return array 統計情報
     */
    public static function getErrorStatistics(string $startTime, string $endTime): array
    {
        $errorLogs = self::getErrorLogsByPeriod($startTime, $endTime);
        $totalLogs = self::getLogsByPeriod($startTime, $endTime);
        
        $errorByCode = $errorLogs->groupBy('error_code')->map->count();
        $errorByProcess = $errorLogs->groupBy('process_name')->map->count();
        
        return [
            'total_logs' => $totalLogs->count(),
            'error_logs' => $errorLogs->count(),
            'error_rate' => $totalLogs->count() > 0 ? 
                round(($errorLogs->count() / $totalLogs->count()) * 100, 2) : 0,
            'error_by_code' => $errorByCode->toArray(),
            'error_by_process' => $errorByProcess->toArray(),
            'period' => [
                'start' => $startTime,
                'end' => $endTime
            ]
        ];
    }

    /**
     * 文字列表現
     * 
     * @return string
     */
    public function __toString(): string
    {
        return sprintf(
            'PrintJobLog[ID:%d, Process:%s, ErrorCode:%d, Time:%s]',
            $this->job_log_id,
            $this->process_name,
            $this->error_code,
            $this->created_at ? $this->created_at->format('Y-m-d H:i:s') : 'N/A'
        );
    }
}
