<?php
declare(strict_types=1);

namespace App\Models;

use PDO;
use Throwable;

class SecurityScanLog extends BaseModel
{
    protected string $table = 'security_scan_logs';

    public function create(array $data): int
    {
        $statement = $this->db()->prepare(
            'INSERT INTO security_scan_logs
                (user_id, target_type, target_id, scan_type, input_value, risk_score, status, reasons, action_taken, created_at)
             VALUES
                (:user_id, :target_type, :target_id, :scan_type, :input_value, :risk_score, :status, :reasons, :action_taken, NOW())'
        );
        $statement->execute([
            'user_id' => $data['user_id'] ?? null,
            'target_type' => $this->nullableString($data['target_type'] ?? null, 80),
            'target_id' => $data['target_id'] ?? null,
            'scan_type' => $this->nullableString($data['scan_type'] ?? 'content', 40),
            'input_value' => $this->nullableString($data['input_value'] ?? null, 500),
            'risk_score' => min(100, max(0, (int) ($data['risk_score'] ?? 0))),
            'status' => $this->validStatus((string) ($data['status'] ?? 'safe')),
            'reasons' => json_encode($data['reasons'] ?? [], JSON_UNESCAPED_UNICODE),
            'action_taken' => $this->validAction((string) ($data['action_taken'] ?? 'allow')),
        ]);

        return (int) $this->db()->lastInsertId();
    }

    public function latest(int $limit = 50): array
    {
        $limit = min(max($limit, 1), 200);

        try {
            $statement = $this->db()->prepare(
                'SELECT id, user_id, target_type, target_id, scan_type, input_value, risk_score, status, reasons, action_taken, created_at
                 FROM security_scan_logs
                 ORDER BY id DESC
                 LIMIT :limit'
            );
            $statement->bindValue('limit', $limit, PDO::PARAM_INT);
            $statement->execute();

            return array_map(fn (array $row): array => $this->normalize($row), $statement->fetchAll());
        } catch (Throwable) {
            return $this->latestLegacy($limit);
        }
    }

    private function latestLegacy(int $limit): array
    {
        try {
            $statement = $this->db()->prepare(
                'SELECT id, user_id, scan_type, subject, result_status, result_reason, result_payload, created_at
                 FROM security_scan_logs
                 ORDER BY id DESC
                 LIMIT :limit'
            );
            $statement->bindValue('limit', $limit, PDO::PARAM_INT);
            $statement->execute();

            return array_map(function (array $row): array {
                $status = match ((string) ($row['result_status'] ?? 'safe')) {
                    'blocked' => 'blocked',
                    'needs_review', 'suspicious' => 'review',
                    default => 'safe',
                };

                return [
                    'id' => (int) $row['id'],
                    'user_id' => isset($row['user_id']) ? (int) $row['user_id'] : null,
                    'target_type' => null,
                    'target_id' => null,
                    'scan_type' => (string) ($row['scan_type'] ?? ''),
                    'input_value' => (string) ($row['subject'] ?? ''),
                    'risk_score' => $status === 'blocked' ? 90 : ($status === 'review' ? 45 : 0),
                    'status' => $status,
                    'reasons' => array_filter([(string) ($row['result_reason'] ?? '')]),
                    'action_taken' => $status === 'blocked' ? 'blocked' : ($status === 'review' ? 'pending_review' : 'allow'),
                    'created_at' => (string) ($row['created_at'] ?? ''),
                ];
            }, $statement->fetchAll());
        } catch (Throwable) {
            return [];
        }
    }

    private function normalize(array $row): array
    {
        $reasons = json_decode((string) ($row['reasons'] ?? '[]'), true);
        if (!is_array($reasons)) {
            $reasons = [];
        }

        return [
            'id' => (int) $row['id'],
            'user_id' => isset($row['user_id']) ? (int) $row['user_id'] : null,
            'target_type' => $row['target_type'] ?? null,
            'target_id' => isset($row['target_id']) ? (int) $row['target_id'] : null,
            'scan_type' => (string) ($row['scan_type'] ?? ''),
            'input_value' => (string) ($row['input_value'] ?? ''),
            'risk_score' => (int) ($row['risk_score'] ?? 0),
            'status' => (string) ($row['status'] ?? 'safe'),
            'reasons' => array_values(array_map('strval', $reasons)),
            'action_taken' => (string) ($row['action_taken'] ?? 'allow'),
            'created_at' => (string) ($row['created_at'] ?? ''),
        ];
    }

    private function nullableString(mixed $value, int $limit): ?string
    {
        if ($value === null) {
            return null;
        }

        $value = trim((string) $value);
        if ($value === '') {
            return null;
        }

        return function_exists('mb_substr') ? mb_substr($value, 0, $limit) : substr($value, 0, $limit);
    }

    private function validStatus(string $status): string
    {
        return in_array($status, ['safe', 'review', 'blocked'], true) ? $status : 'safe';
    }

    private function validAction(string $action): string
    {
        return in_array($action, ['allow', 'pending_review', 'blocked'], true) ? $action : 'allow';
    }
}
