<?php

declare(strict_types=1);

namespace App\Services\Compliance;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * ISMS-P 28 automated controls.
 *
 * Each control resolves to a public method `check_<code>()` returning:
 *   ['status' => pass|fail|skip|error, 'evidence' => array, 'notes' => string|null]
 *
 * Usage:
 *   $report = app(IsmsControls::class)->runAll();
 *   // Persisted to isms_check_runs table by CompliancePiaReport command.
 */
class IsmsControls
{
    /**
     * 28 controls aligned with KISA ISMS-P (2026 revision).
     * code => [category, title, severity]
     */
    public const CONTROLS = [
        '1.1.1' => ['policy',       '정보보호 정책 수립',                  'high'],
        '1.1.2' => ['policy',       '정책 검토·승인 절차',                 'medium'],
        '1.2.1' => ['organization', '정보보호 조직 구성',                  'medium'],
        '2.1.1' => ['asset',        '자산 식별 및 분류',                   'high'],
        '2.2.1' => ['asset',        '개인정보 영향평가(PIA) 수행',         'critical'],
        '2.3.1' => ['risk',         '위험 평가 정기 수행',                 'high'],
        '3.1.1' => ['hr',           '임직원 보안 서약',                    'medium'],
        '3.2.1' => ['hr',           '보안 교육 연 1회 이상',               'medium'],
        '4.1.1' => ['physical',     '물리적 출입 통제',                    'high'],
        '5.1.1' => ['access',       '계정 생성·삭제 절차',                 'high'],
        '5.1.2' => ['access',       '최소 권한 원칙 적용 (RBAC)',          'critical'],
        '5.2.1' => ['access',       '비밀번호 복잡도 정책',                'high'],
        '5.2.2' => ['access',       '2FA / MFA 적용',                      'critical'],
        '5.3.1' => ['access',       '세션 타임아웃 (≤ 30분)',              'medium'],
        '6.1.1' => ['crypto',       '암호화 키 관리',                      'critical'],
        '6.1.2' => ['crypto',       '저장 데이터 암호화 (AES-256)',        'critical'],
        '6.2.1' => ['crypto',       '전송 데이터 암호화 (TLS 1.2+)',       'critical'],
        '7.1.1' => ['ops',          '백업 일일 수행 + 복구 테스트',        'high'],
        '7.2.1' => ['ops',          '취약점 점검 분기 1회',                'high'],
        '7.3.1' => ['ops',          '시스템 패치 30일 이내',               'high'],
        '8.1.1' => ['network',      '방화벽 정책 운영',                    'high'],
        '8.2.1' => ['network',      '침입 탐지·차단 (IDS/IPS)',            'high'],
        '9.1.1' => ['incident',     '침해 사고 대응 절차',                 'critical'],
        '9.2.1' => ['incident',     '로그 보관 (감사 로그 90일+)',         'critical'],
        '10.1.1'=> ['privacy',      'PIPA 동의 관리',                      'critical'],
        '10.2.1'=> ['privacy',      '데이터 주체 권리 (조회/삭제/이동)',   'critical'],
        '10.3.1'=> ['privacy',      '개인정보 90일 보관 + 자동 파기',      'high'],
        '11.1.1'=> ['continuity',   '업무 연속성 계획 (BCP)',              'high'],
    ];

    public function runAll(): array
    {
        $results = [];
        foreach (array_keys(self::CONTROLS) as $code) {
            $results[$code] = $this->runOne($code);
        }

        return $results;
    }

    public function runOne(string $code): array
    {
        $method = 'check_'.str_replace('.', '_', $code);

        if (! method_exists($this, $method)) {
            return ['status' => 'skip', 'evidence' => [], 'notes' => 'verifier not implemented'];
        }

        try {
            return $this->{$method}();
        } catch (\Throwable $e) {
            return ['status' => 'error', 'evidence' => [], 'notes' => $e->getMessage()];
        }
    }

    // ----- automated verifiers (subset; expand in Sprint 6+) -----

    public function check_2_2_1(): array
    {
        $exists = Schema::hasTable('consent_records');

        return [
            'status' => $exists ? 'pass' : 'fail',
            'evidence' => ['consent_records_exists' => $exists],
            'notes' => 'PIA prerequisite: consent ledger schema present.',
        ];
    }

    public function check_5_1_2(): array
    {
        // RBAC = spatie/laravel-permission tables.
        $ok = Schema::hasTable('roles') && Schema::hasTable('permissions');

        return [
            'status' => $ok ? 'pass' : 'fail',
            'evidence' => ['spatie_tables' => $ok],
            'notes' => 'RBAC enforced via spatie/laravel-permission.',
        ];
    }

    public function check_5_2_1(): array
    {
        $rule = (string) config('compliance.password_rule', 'min:8|mixed|symbol');

        return [
            'status' => str_contains($rule, 'min:') ? 'pass' : 'fail',
            'evidence' => ['rule' => $rule],
            'notes' => 'Password policy from compliance.password_rule.',
        ];
    }

    public function check_5_2_2(): array
    {
        $ok = Schema::hasColumn('users', 'two_factor_secret');

        return [
            'status' => $ok ? 'pass' : 'fail',
            'evidence' => ['two_factor_column' => $ok],
            'notes' => 'TOTP secret column present; enforcement at controller layer.',
        ];
    }

    public function check_6_2_1(): array
    {
        $force = (bool) config('session.secure', false);

        return [
            'status' => app()->isProduction() ? ($force ? 'pass' : 'fail') : 'skip',
            'evidence' => ['session.secure' => $force],
            'notes' => 'TLS forced for cookies in production.',
        ];
    }

    public function check_9_2_1(): array
    {
        $count = DB::table('audit_logs')
            ->where('created_at', '>=', now()->subDays(90))
            ->count();

        return [
            'status' => Schema::hasTable('audit_logs') ? 'pass' : 'fail',
            'evidence' => ['rows_last_90d' => $count],
            'notes' => '90-day audit retention; pruning handled by scheduler.',
        ];
    }

    public function check_10_1_1(): array
    {
        $ok = Schema::hasTable('consent_records');

        return [
            'status' => $ok ? 'pass' : 'fail',
            'evidence' => ['consent_records_exists' => $ok],
            'notes' => 'PIPA consent ledger present.',
        ];
    }
}
