<?php

use Acms\Services\Logger\Filter;
use Acms\Services\Logger\LogRecordScrubber;

class ACMS_POST_Logger_Info extends ACMS_POST
{
    public function post()
    {
        try {
            $id = $this->Post->get('id');
            $this->validate($id);

            $sql = SQL::newSelect('audit_log');
            $sql->addWhereOpr('audit_log_id', $id);
            $log = DB::query($sql->get(dsn()), 'row');
            if (empty($log)) {
                throw new \RuntimeException('ログが存在しません');
            }
            $data = $this->buildData($log);
            $data['success'] = true;
            Response::json($data);
        } catch (\Exception $e) {
            Response::json([
                'success' => false,
                'message' => $e->getMessage(),
            ]);
        }
    }

    /**
     * @param int $id
     * @return void
     * @throws RuntimeException
     */
    protected function validate(int $id): void
    {
        // 監査ログはルートブログ限定の機能。BID を渡すと isControlBlog() が自ブログと配下を
        // 許すため子ブログ管理者が自分の BID で通過してしまうので、RBID を渡して
        // 「ルートブログの管理者か」を判定する。
        if (!sessionWithAdministration(RBID)) {
            throw new \RuntimeException('アクセス権限がありません');
        }
        if (empty($id)) {
            throw new \RuntimeException('パラメータが間違っています');
        }
    }

    /**
     * ログデータを組み立て
     *
     * Why not: 既存の監査ログレコードには本 PR 以前に書き込まれた平文のトークン・セッション情報が
     * 残っている可能性があるため、遡及 UPDATE（ロールバック不可・監査ログの改変になる）ではなく
     * 読み出し時に Filter でマスクする方針にしている。マスク適用そのものは LogRecordScrubber へ
     * 切り出し済み（ACMS_RAM 等の環境依存を含むこのメソッドを直接単体テストしにくいため）。
     *
     * @param array $log
     * @return array
     */
    protected function buildData(array $log): array
    {
        /** @var Filter $filter */
        $filter = App::make('acms-logger-filter');
        $scrubbed = (new LogRecordScrubber())->scrub($log, $filter);

        $suid = $log['audit_log_session_uid'];
        $data = [
            'id' => $log['audit_log_id'],
            'datetime' => $log['audit_log_datetime'],
            'level' => $log['audit_log_level'],
            'levelName' => $log['audit_log_level_name'],
            'suid' => $suid,
            'sessionUserName' => ACMS_RAM::userName($suid),
            'sessionUserMail' => ACMS_RAM::userMail($suid),
            'bid' => $log['audit_log_blog_id'],
            'blogName' => ACMS_RAM::blogName($log['audit_log_blog_id']),
            'message' => $log['audit_log_message'],
            'url' => $scrubbed['url'],
            'isManagedDomain' => $this->isManagedDomain($log['audit_log_url'] ?? ''),
            'ua' => $log['audit_log_ua'],
            'referer' => $scrubbed['referer'],
            'ipAddress' => $log['audit_log_addr'],
            'method' => $log['audit_log_method'],
            'httpStatus' => $log['audit_log_status'],
            'responseTime' => $log['audit_log_response_time'],
            'eid' => $log['audit_log_eid'],
            'cid' => $log['audit_log_cid'],
            'uid' => $log['audit_log_uid'],
            'rid' => $log['audit_log_rid'],
            'ruleName' => ACMS_RAM::ruleName($log['audit_log_rid']),
            'extra' => $scrubbed['extra'],
            'context' => $scrubbed['context'],
        ];
        if ($altUid = $log['audit_log_session_alt_uid']) {
            $data['switchUserName'] = ACMS_RAM::userName($altUid);
            $data['switchUserMail'] = ACMS_RAM::userMail($altUid);
        }
        if ($scrubbed['reqHeader'] !== null) {
            $data['reqHeader'] = $scrubbed['reqHeader'];
            $data['reqBody'] = $scrubbed['reqBody'];
        }
        if ($post = $log['audit_log_acms_post']) {
            $data['acmsPost'] = $post;
        }
        return $data;
    }

    /**
     * 指定されたURLがCMSで管理しているドメインか判定する
     *
     * @param string $url
     * @return bool
     */
    protected function isManagedDomain(string $url): bool
    {
        $managedDomainRegistry = App::make('url.managed-domain');
        assert($managedDomainRegistry instanceof \Acms\Services\Url\ManagedDomainRegistry);
        return $managedDomainRegistry->isManagedDomain($url);
    }
}
