<?php

declare(strict_types=1);

namespace Kylin\Medical\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Crypt;

/**
 * 患者模型
 *
 * 医疗系统患者信息，关联用户账号和管理医生。
 *
 * 法律合规：
 *  - 依据《个人信息保护法》第二十八条，医疗健康信息属于敏感个人信息，
 *    对 allergy_history / address 字段进行加密存储。
 *  - 依据《电子病历应用管理规范》第十条，电子病历应使用电子签名。
 */
class Patient extends Model
{
    protected $table = 'patients';

    protected $fillable = [
        'user_id',
        'medical_no',
        'is_first_visit',
        'blood_type',
        'allergy_history',
        'address',
    ];

    protected $casts = [
        'is_first_visit' => 'boolean',
    ];

    /**
     * 需加密的字段（依据《个人信息保护法》第二十八条）
     */
    private const ENCRYPTED_FIELDS = ['allergy_history', 'address'];

    /**
     * 关联用户
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(config('auth.providers.users.model', \App\Models\User::class), 'user_id');
    }

    /**
     * 设置过敏史（加密存储）
     *
     * 依据《个人信息保护法》第二十八条，医疗健康敏感信息需加密存储。
     */
    public function setAllergyHistoryAttribute(?string $value): void
    {
        $this->attributes['allergy_history'] = $value === null ? null : Crypt::encryptString($value);
    }

    /**
     * 获取过敏史（解密）
     */
    public function getAllergyHistoryAttribute(?string $value): ?string
    {
        return $value === null ? null : $this->decryptSafely($value);
    }

    /**
     * 设置住址（加密存储）
     */
    public function setAddressAttribute(?string $value): void
    {
        $this->attributes['address'] = $value === null ? null : Crypt::encryptString($value);
    }

    /**
     * 获取住址（解密）
     */
    public function getAddressAttribute(?string $value): ?string
    {
        return $value === null ? null : $this->decryptSafely($value);
    }

    /**
     * 安全解密（兼容历史未加密数据）
     */
    private function decryptSafely(string $value): ?string
    {
        try {
            return Crypt::decryptString($value);
        } catch (\Throwable $e) {
            // 兼容历史未加密数据，原样返回
            return $value;
        }
    }

    /**
     * 转数组时对加密字段脱敏（依据《个人信息保护法》第二十八条）
     *
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        $array = parent::toArray();

        foreach (self::ENCRYPTED_FIELDS as $field) {
            if (isset($array[$field]) && is_string($array[$field])) {
                $array[$field] = $this->maskSensitive($array[$field]);
            }
        }

        return $array;
    }

    /**
     * 敏感字段脱敏展示
     */
    private function maskSensitive(string $value): string
    {
        $length = mb_strlen($value);
        return match (true) {
            $length <= 2 => str_repeat('*', $length),
            $length <= 6 => mb_substr($value, 0, 1) . str_repeat('*', $length - 2) . mb_substr($value, -1),
            default => mb_substr($value, 0, 2) . str_repeat('*', 4) . mb_substr($value, -2),
        };
    }
}
